blob: 88db725d241942625c7ff99dbf82bd065a41235b [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 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "threadloop.h"
22
23#include <ciso646> // fix windows compiler bug
24
25namespace jami {
26
27void
28ThreadLoop::mainloop(std::thread::id& tid,
29 const std::function<bool()> setup,
30 const std::function<void()> process,
31 const std::function<void()> cleanup)
32{
33 tid = std::this_thread::get_id();
34 try {
35 if (setup()) {
36 while (state_ == ThreadState::RUNNING)
37 process();
38 cleanup();
39 } else {
40 throw std::runtime_error("setup failed");
41 }
42 } catch (const ThreadLoopException& e) {
43 if (logger_) logger_->e("[threadloop:{}] ThreadLoopException: {}", fmt::ptr(this), e.what());
44 } catch (const std::exception& e) {
45 if (logger_) logger_->e("[threadloop:{}] Unwaited exception: {}", fmt::ptr(this), e.what());
46 }
47 stop();
48}
49
50ThreadLoop::ThreadLoop(std::shared_ptr<dht::log::Logger> logger,
51 const std::function<bool()>& setup,
52 const std::function<void()>& process,
53 const std::function<void()>& cleanup)
54 : setup_(setup)
55 , process_(process)
56 , cleanup_(cleanup)
57 , thread_()
58 , logger_(std::move(logger))
59{}
60
61ThreadLoop::~ThreadLoop()
62{
63 if (isRunning()) {
64 if (logger_) logger_->error("join() should be explicitly called in owner's destructor");
65 join();
66 }
67}
68
69void
70ThreadLoop::start()
71{
72 const auto s = state_.load();
73
74 if (s == ThreadState::RUNNING) {
75 if (logger_) logger_->error("already started");
76 return;
77 }
78
79 // stop pending but not processed by thread yet?
80 if (s == ThreadState::STOPPING and thread_.joinable()) {
81 if (logger_) logger_->debug("stop pending");
82 thread_.join();
83 }
84
85 state_ = ThreadState::RUNNING;
86 thread_ = std::thread(&ThreadLoop::mainloop, this, std::ref(threadId_), setup_, process_, cleanup_);
87 threadId_ = thread_.get_id();
88}
89
90void
91ThreadLoop::stop()
92{
93 if (state_ == ThreadState::RUNNING)
94 state_ = ThreadState::STOPPING;
95}
96
97void
98ThreadLoop::join()
99{
100 stop();
101 if (thread_.joinable())
102 thread_.join();
103}
104
105void
106ThreadLoop::waitForCompletion()
107{
108 if (thread_.joinable())
109 thread_.join();
110}
111
112void
113ThreadLoop::exit()
114{
115 stop();
116 throw ThreadLoopException();
117}
118
119bool
120ThreadLoop::isRunning() const noexcept
121{
122#ifdef _WIN32
123 return state_ == ThreadState::RUNNING;
124#else
125 return thread_.joinable() and state_ == ThreadState::RUNNING;
126#endif
127}
128
129void
130InterruptedThreadLoop::stop()
131{
132 ThreadLoop::stop();
133 cv_.notify_one();
134}
135} // namespace jami