blob: de4e475bf9802c743d516df2ac0087ec3cda4135 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
5 * Author: Eloi Bail <Eloi.Bail@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#pragma once
23
24#include <atomic>
25#include <thread>
26#include <functional>
27#include <stdexcept>
28#include <condition_variable>
29#include <mutex>
30
31#include <opendht/logger.h>
32
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040033namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040034
35struct ThreadLoopException : public std::runtime_error
36{
37 ThreadLoopException()
38 : std::runtime_error("ThreadLoopException")
39 {}
40};
41
42class ThreadLoop
43{
44public:
45 enum class ThreadState { READY, RUNNING, STOPPING };
46
47 ThreadLoop(std::shared_ptr<dht::log::Logger> logger,
48 const std::function<bool()>& setup,
49 const std::function<void()>& process,
50 const std::function<void()>& cleanup);
51 virtual ~ThreadLoop();
52
53 void start();
54 void exit();
55 virtual void stop();
56 void join();
57 void waitForCompletion(); // thread will stop itself
58
59 bool isRunning() const noexcept;
60 bool isStopping() const noexcept { return state_ == ThreadState::STOPPING; }
61 std::thread::id get_id() const noexcept { return threadId_; }
62
63private:
64 ThreadLoop(const ThreadLoop&) = delete;
65 ThreadLoop(ThreadLoop&&) noexcept = delete;
66 ThreadLoop& operator=(const ThreadLoop&) = delete;
67 ThreadLoop& operator=(ThreadLoop&&) noexcept = delete;
68
69 // These must be provided by users of ThreadLoop
70 std::function<bool()> setup_;
71 std::function<void()> process_;
72 std::function<void()> cleanup_;
73
74 void mainloop(std::thread::id& tid,
75 const std::function<bool()> setup,
76 const std::function<void()> process,
77 const std::function<void()> cleanup);
78
79 std::atomic<ThreadState> state_ {ThreadState::READY};
80 std::thread::id threadId_;
81 std::thread thread_;
82 std::shared_ptr<dht::log::Logger> logger_;
83};
84
85class InterruptedThreadLoop : public ThreadLoop
86{
87public:
88 InterruptedThreadLoop(std::shared_ptr<dht::log::Logger> logger,
89 const std::function<bool()>& setup,
90 const std::function<void()>& process,
91 const std::function<void()>& cleanup)
92 : ThreadLoop::ThreadLoop(logger, setup, process, cleanup)
93 {}
94
95 void stop() override;
96
97 void interrupt() noexcept { cv_.notify_one(); }
98
99 template<typename Rep, typename Period>
100 void wait_for(const std::chrono::duration<Rep, Period>& rel_time)
101 {
102 if (std::this_thread::get_id() != get_id())
103 throw std::runtime_error("can not call wait_for outside thread context");
104
105 std::unique_lock<std::mutex> lk(mutex_);
106 cv_.wait_for(lk, rel_time, [this]() { return isStopping(); });
107 }
108
109 template<typename Rep, typename Period, typename Pred>
110 bool wait_for(const std::chrono::duration<Rep, Period>& rel_time, Pred&& pred)
111 {
112 if (std::this_thread::get_id() != get_id())
113 throw std::runtime_error("can not call wait_for outside thread context");
114
115 std::unique_lock<std::mutex> lk(mutex_);
116 return cv_.wait_for(lk, rel_time, [this, pred] { return isStopping() || pred(); });
117 }
118
119 template<typename Pred>
120 void wait(Pred&& pred)
121 {
122 if (std::this_thread::get_id() != get_id())
123 throw std::runtime_error("Can not call wait outside thread context");
124
125 std::unique_lock<std::mutex> lk(mutex_);
126 cv_.wait(lk, [this, p = std::forward<Pred>(pred)] { return isStopping() || p(); });
127 }
128
129private:
130 std::mutex mutex_;
131 std::condition_variable cv_;
132};
133
134} // namespace jami