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