blob: 3686991f6ab9b9ba6584cea874e8e9d7e9e8a329 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (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
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
Adrien Béraudcb753622023-07-17 22:32:49 -040015 * along with this program. If not, see <https://www.gnu.org/licenses/>.
Adrien Béraud612b55b2023-05-29 10:42:04 -040016 */
Adrien Béraud612b55b2023-05-29 10:42:04 -040017#include "threadloop.h"
18
19#include <ciso646> // fix windows compiler bug
20
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040021namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040022
23void
24ThreadLoop::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 tid = std::this_thread::get_id();
30 try {
31 if (setup()) {
32 while (state_ == ThreadState::RUNNING)
33 process();
34 cleanup();
35 } else {
36 throw std::runtime_error("setup failed");
37 }
38 } catch (const ThreadLoopException& e) {
39 if (logger_) logger_->e("[threadloop:{}] ThreadLoopException: {}", fmt::ptr(this), e.what());
40 } catch (const std::exception& e) {
41 if (logger_) logger_->e("[threadloop:{}] Unwaited exception: {}", fmt::ptr(this), e.what());
42 }
43 stop();
44}
45
46ThreadLoop::ThreadLoop(std::shared_ptr<dht::log::Logger> logger,
47 const std::function<bool()>& setup,
48 const std::function<void()>& process,
49 const std::function<void()>& cleanup)
50 : setup_(setup)
51 , process_(process)
52 , cleanup_(cleanup)
53 , thread_()
54 , logger_(std::move(logger))
55{}
56
57ThreadLoop::~ThreadLoop()
58{
59 if (isRunning()) {
60 if (logger_) logger_->error("join() should be explicitly called in owner's destructor");
61 join();
62 }
63}
64
65void
66ThreadLoop::start()
67{
68 const auto s = state_.load();
69
70 if (s == ThreadState::RUNNING) {
71 if (logger_) logger_->error("already started");
72 return;
73 }
74
75 // stop pending but not processed by thread yet?
76 if (s == ThreadState::STOPPING and thread_.joinable()) {
77 if (logger_) logger_->debug("stop pending");
78 thread_.join();
79 }
80
81 state_ = ThreadState::RUNNING;
82 thread_ = std::thread(&ThreadLoop::mainloop, this, std::ref(threadId_), setup_, process_, cleanup_);
83 threadId_ = thread_.get_id();
84}
85
86void
87ThreadLoop::stop()
88{
89 if (state_ == ThreadState::RUNNING)
90 state_ = ThreadState::STOPPING;
91}
92
93void
94ThreadLoop::join()
95{
96 stop();
97 if (thread_.joinable())
98 thread_.join();
99}
100
101void
102ThreadLoop::waitForCompletion()
103{
104 if (thread_.joinable())
105 thread_.join();
106}
107
108void
109ThreadLoop::exit()
110{
111 stop();
112 throw ThreadLoopException();
113}
114
115bool
116ThreadLoop::isRunning() const noexcept
117{
118#ifdef _WIN32
119 return state_ == ThreadState::RUNNING;
120#else
121 return thread_.joinable() and state_ == ThreadState::RUNNING;
122#endif
123}
124
125void
126InterruptedThreadLoop::stop()
127{
128 ThreadLoop::stop();
129 cv_.notify_one();
130}
Sébastien Blin464bdff2023-07-19 08:02:53 -0400131} // namespace dhtnet