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