add initial project structure
Change-Id: I6a3fb080ff623b312e42d71754480a7ce00b81a0
diff --git a/src/security/threadloop.h b/src/security/threadloop.h
new file mode 100644
index 0000000..8a7a0c6
--- /dev/null
+++ b/src/security/threadloop.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
+ *
+ * Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
+ * Author: Eloi Bail <Eloi.Bail@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <thread>
+#include <functional>
+#include <stdexcept>
+#include <condition_variable>
+#include <mutex>
+
+#include <opendht/logger.h>
+
+namespace jami {
+
+struct ThreadLoopException : public std::runtime_error
+{
+ ThreadLoopException()
+ : std::runtime_error("ThreadLoopException")
+ {}
+};
+
+class ThreadLoop
+{
+public:
+ enum class ThreadState { READY, RUNNING, STOPPING };
+
+ ThreadLoop(std::shared_ptr<dht::log::Logger> logger,
+ const std::function<bool()>& setup,
+ const std::function<void()>& process,
+ const std::function<void()>& cleanup);
+ virtual ~ThreadLoop();
+
+ void start();
+ void exit();
+ virtual void stop();
+ void join();
+ void waitForCompletion(); // thread will stop itself
+
+ bool isRunning() const noexcept;
+ bool isStopping() const noexcept { return state_ == ThreadState::STOPPING; }
+ std::thread::id get_id() const noexcept { return threadId_; }
+
+private:
+ ThreadLoop(const ThreadLoop&) = delete;
+ ThreadLoop(ThreadLoop&&) noexcept = delete;
+ ThreadLoop& operator=(const ThreadLoop&) = delete;
+ ThreadLoop& operator=(ThreadLoop&&) noexcept = delete;
+
+ // These must be provided by users of ThreadLoop
+ std::function<bool()> setup_;
+ std::function<void()> process_;
+ std::function<void()> cleanup_;
+
+ void mainloop(std::thread::id& tid,
+ const std::function<bool()> setup,
+ const std::function<void()> process,
+ const std::function<void()> cleanup);
+
+ std::atomic<ThreadState> state_ {ThreadState::READY};
+ std::thread::id threadId_;
+ std::thread thread_;
+ std::shared_ptr<dht::log::Logger> logger_;
+};
+
+class InterruptedThreadLoop : public ThreadLoop
+{
+public:
+ InterruptedThreadLoop(std::shared_ptr<dht::log::Logger> logger,
+ const std::function<bool()>& setup,
+ const std::function<void()>& process,
+ const std::function<void()>& cleanup)
+ : ThreadLoop::ThreadLoop(logger, setup, process, cleanup)
+ {}
+
+ void stop() override;
+
+ void interrupt() noexcept { cv_.notify_one(); }
+
+ template<typename Rep, typename Period>
+ void wait_for(const std::chrono::duration<Rep, Period>& rel_time)
+ {
+ if (std::this_thread::get_id() != get_id())
+ throw std::runtime_error("can not call wait_for outside thread context");
+
+ std::unique_lock<std::mutex> lk(mutex_);
+ cv_.wait_for(lk, rel_time, [this]() { return isStopping(); });
+ }
+
+ template<typename Rep, typename Period, typename Pred>
+ bool wait_for(const std::chrono::duration<Rep, Period>& rel_time, Pred&& pred)
+ {
+ if (std::this_thread::get_id() != get_id())
+ throw std::runtime_error("can not call wait_for outside thread context");
+
+ std::unique_lock<std::mutex> lk(mutex_);
+ return cv_.wait_for(lk, rel_time, [this, pred] { return isStopping() || pred(); });
+ }
+
+ template<typename Pred>
+ void wait(Pred&& pred)
+ {
+ if (std::this_thread::get_id() != get_id())
+ throw std::runtime_error("Can not call wait outside thread context");
+
+ std::unique_lock<std::mutex> lk(mutex_);
+ cv_.wait(lk, [this, p = std::forward<Pred>(pred)] { return isStopping() || p(); });
+ }
+
+private:
+ std::mutex mutex_;
+ std::condition_variable cv_;
+};
+
+} // namespace jami