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 :
18 : #include "threadloop.h"
19 : #include "logger.h"
20 :
21 : namespace jami {
22 :
23 : void
24 935 : ThreadLoop::mainloop(std::thread::id& tid,
25 : const std::function<bool()>& setup,
26 : const std::function<void()>& process,
27 : const std::function<void()>& cleanup)
28 : {
29 935 : tid = std::this_thread::get_id();
30 : try {
31 935 : if (setup()) {
32 52924 : while (state_ == ThreadState::RUNNING)
33 51990 : process();
34 934 : cleanup();
35 : } else {
36 0 : JAMI_ERROR("setup failed");
37 : }
38 1 : } catch (const ThreadLoopException& e) {
39 0 : JAMI_ERROR("[threadloop:{}] ThreadLoopException: {}", fmt::ptr(this), e.what());
40 1 : } catch (const std::exception& e) {
41 4 : JAMI_ERROR("[threadloop:{}] Unwaited exception: {}", fmt::ptr(this), e.what());
42 1 : }
43 935 : stop();
44 935 : }
45 :
46 1340 : ThreadLoop::ThreadLoop(const std::function<bool()>& setup,
47 : const std::function<void()>& process,
48 1340 : const std::function<void()>& cleanup)
49 1340 : : setup_(setup)
50 1340 : , process_(process)
51 1340 : , cleanup_(cleanup)
52 2680 : , thread_()
53 1340 : {}
54 :
55 1340 : ThreadLoop::~ThreadLoop()
56 : {
57 1340 : if (isRunning()) {
58 0 : JAMI_ERROR("join() should be explicitly called in owner's destructor");
59 0 : join();
60 : }
61 1340 : }
62 :
63 : void
64 940 : ThreadLoop::start()
65 : {
66 940 : const auto s = state_.load();
67 :
68 940 : if (s == ThreadState::RUNNING) {
69 20 : JAMI_ERROR("already started");
70 5 : return;
71 : }
72 :
73 : // stop pending but not processed by thread yet?
74 935 : if (s == ThreadState::STOPPING and thread_.joinable()) {
75 52 : JAMI_LOG("stop pending");
76 13 : thread_.join();
77 : }
78 :
79 935 : state_ = ThreadState::RUNNING;
80 935 : thread_ = std::thread(&ThreadLoop::mainloop, this, std::ref(threadId_), setup_, process_, cleanup_);
81 935 : threadId_ = thread_.get_id();
82 : }
83 :
84 : void
85 3006 : ThreadLoop::stop()
86 : {
87 3006 : auto expected = ThreadState::RUNNING;
88 3006 : state_.compare_exchange_strong(expected, ThreadState::STOPPING);
89 3006 : }
90 :
91 : void
92 1599 : ThreadLoop::join()
93 : {
94 1599 : stop();
95 1599 : if (thread_.joinable())
96 922 : thread_.join();
97 1599 : }
98 :
99 : void
100 0 : ThreadLoop::waitForCompletion()
101 : {
102 0 : if (thread_.joinable())
103 0 : thread_.join();
104 0 : }
105 :
106 : void
107 0 : ThreadLoop::exit()
108 : {
109 0 : stop();
110 0 : throw ThreadLoopException();
111 : }
112 :
113 : bool
114 17508 : ThreadLoop::isRunning() const noexcept
115 : {
116 : #ifdef _WIN32
117 : return state_ == ThreadState::RUNNING;
118 : #else
119 17508 : return thread_.joinable() and state_ == ThreadState::RUNNING;
120 : #endif
121 : }
122 :
123 : void
124 1126 : InterruptedThreadLoop::stop()
125 : {
126 1126 : ThreadLoop::stop();
127 1126 : cv_.notify_one();
128 1126 : }
129 : } // namespace jami
|