Fixposition SDK 0.0.0-heads/main-0-gd0a6ce2
Collection of c++ libraries and apps for use with Fixposition products
Loading...
Searching...
No Matches
thread.hpp
Go to the documentation of this file.
1/**
2 * \verbatim
3 * ___ ___
4 * \ \ / /
5 * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors
6 * / /\ \ License: see the LICENSE file
7 * /__/ \__\
8 * \endverbatim
9 *
10 * @file
11 * @brief Fixposition SDK: Thread helpers
12 *
13 * @page FPSDK_COMMON_THREAD Thread helpers
14 *
15 * **API**: fpsdk_common/thread.hpp and fpsdk::common::thread
16 *
17 */
18#ifndef __FPSDK_COMMON_THREAD_HPP__
19#define __FPSDK_COMMON_THREAD_HPP__
20
21/* LIBC/STL */
22#include <atomic>
23#include <condition_variable>
24#include <cstdint>
25#include <functional>
26#include <memory>
27#include <mutex>
28#include <string>
29#include <thread>
30
31/* EXTERNAL */
32
33/* PACKAGE */
34
35namespace fpsdk {
36namespace common {
37/**
38 * @brief Thread helpers
39 */
40namespace thread {
41/* ****************************************************************************************************************** */
42
43/**
44 * @brief Thread sleep result
45 */
46enum class WaitRes
47{
48 WOKEN, //!< Thread was woken up (sleep interrupted), or semaphore was taken (wait interrupted)
49 TIMEOUT, //!< Sleep or wait timeout has expired (no wakeup, no interrupt)
50};
51
52/**
53 * @brief A binary semaphore, useful for thread synchronisation
54 *
55 * The default state is "taken", that is, WaitFor() and WaitUntil() block (sleep) until the semaphore is "given" using
56 * Notify(). See the example in the Thread class documentation below.
57 */
59{
60 public:
62
63 /**
64 * @brief Notify ("signal", "give")
65 */
66 void Notify();
67
68 /**
69 * @brief Wait (take), with timeout
70 *
71 * @param[in] millis Number of [ms] to sleep, must be > 0
72 *
73 * @returns WaitRes::WOKEN if taken (within time limit), WaitRes::TIMEOUT if the timeout has expired
74 */
75 WaitRes WaitFor(const uint32_t millis);
76
77 /**
78 * @brief Wait (take), with timout aligned to a period
79 *
80 * This blocks (sleeps) until the start of the next millis period is reached (or the semaphore has been taken). The
81 * period is aligned to the system clock. For example, with a period of 500ms it would timeout at t+0.0s, t+0.5s,
82 * t+1.0s, t+1.5s, etc. If the calculated wait duration is less then the given minimal wait duration, it waits
83 * longer than one period in order to achieve the minimal wait duration and still timeout on the start of a period.
84 *
85 * Note that this works on the actual system clock. In ROS replay scenario this may not behave as expected as the
86 * fake ROS system clock may be faster or slower than the actual system clock.
87 *
88 * @param[in] period Period duration [ms], must be > 0
89 * @param[in] min_sleep Minimal sleep duration [ms], must be < period
90 *
91 * @returns WaitRes::WOKEN if taken (within time limit), WaitRes::TIMEOUT if the timeout has expired or period was 0
92 */
93 WaitRes WaitUntil(const uint32_t period, const uint32_t min_sleep = 0);
94
95 private:
96 std::mutex mutex_; //!< Mutex
97 std::condition_variable cond_; //!< Condition
98};
99
100// ---------------------------------------------------------------------------------------------------------------------
101
102/**
103 * @brief Helper class for handling threads
104 *
105 * Example:
106 *
107 * @code{.cpp}
108 * class Example {
109 * public:
110 *
111 * Example() :
112 * thread_ { "worker", std::bind(&Example::Worker, this, std::placeholders::_1) }
113 * { }
114 *
115 * void Run() {
116 *
117 * if (!thread_.Start()) {
118 * throw "...";
119 * }
120 *
121 * int n = 0;
122 * while (thread_.GetStatus() == Thread::Status::RUNNING) {
123 *
124 * sleep(500);
125 * thread_.Wakeup() // = BinarySemaphore::Notify()
126 *
127 * sleep(2000);
128 * thread_.Wakeup() // = BinarySemaphore::Notify()
129 *
130 * n++;
131 * if (n >= 10) {
132 * thread_.Stop();
133 * }
134 * }
135 * }
136 *
137 * private:
138 *
139 * bool Worker(void *) {
140 * while (!thread_.ShouldAbort()) {
141 * if (thread_.Sleep(123) == WaitRes::WOKEN) { // = BinarySemaphore::SleepFor(1000)
142 * // We have been woken up
143 * } else {
144 * // Timeout expired
145 * }
146 * }
147 * return true;
148 * }
149 *
150 * Thread thread_;
151 * };
152 * @endcode
153 */
155{
156 public:
157 using ThreadFunc = std::function<bool(Thread*, void*)>; //!< Thread main function
158 using PrepFunc = std::function<void(void*)>; //!< Thread prepare function
159 using CleanFunc = std::function<void(void*)>; //!< Thread cleanup function
160
161 /**
162 * @brief Constructor
163 *
164 * @param[in] name Name of the thread, for debugging
165 * @param[in] func The thread function
166 * @param[in] arg Optional thread function user argument
167 * @param[in] prep Optional prepare function, called before the thread function
168 * @param[in] clean Optional cleanup function, called after the threadd stopped (or crashed)
169 *
170 * @note Constructing an instance delibarately does not automatically start the thread!
171 */
172 Thread(const std::string& name, ThreadFunc func, void* arg = nullptr, PrepFunc prep = nullptr,
173 CleanFunc clean = nullptr);
174
175 /**
176 * @brief Destructor, blocks until thread has stopped
177 */
179
180 // ----- Common methods -----
181
182 /**
183 * @brief Get thread name
184 *
185 * @returns the thread name
186 */
187 const std::string& GetName();
188
189 // -----------------------------------------------------------------------------------------------------------------
190
191 /**
192 * @name Main (controlling) thread methods
193 * @{
194 */
195
196 /**
197 * @brief Start the thread
198 *
199 * @param[in] try_catch Run user-supplied thread function in a try ... catch block
200 *
201 * @returns true if the thread was started, false otherwise
202 */
203 bool Start(const bool try_catch = true);
204
205 /**
206 * @brief Stop the thread
207 *
208 * @returns true if the thread was stopped, false otherwise
209 */
210 bool Stop();
211
212 /**
213 * @brief Wakup a sleeping thread
214 */
215 void Wakeup();
216
217 /**
218 * @brief Thread status
219 */
221 {
222 STOPPED, //!< Stopped (not Start()ed, or properly and happily Stop()ped)
223 RUNNING, //!< Running (Start(ed) and happily running)
224 FAILED, //!< Failed (was Start()ed, but crashed due to an exception)
225 };
226
227 /**
228 * @brief Check thread status
229 *
230 * @returns the thread status
231 */
233
234 //@}
235
236 // -----------------------------------------------------------------------------------------------------------------
237
238 /**
239 * @name Worker thread methods
240 * @{
241 */
242
243 /**
244 * @brief Sleep until timeout or woken up
245 *
246 * @param[in] millis Number of [ms] to sleep
247 *
248 * @returns WaitRes::WOKEN if the thread has been woken up, WaitRes::TIMEOUT if the timeout has expired
249 */
250 WaitRes Sleep(const uint32_t millis);
251
252 /**
253 * @brief Sleep until next period start or woken up
254 *
255 * See BinarySemaphore::WaitUntil() for a detailed explanation.
256 *
257 * @param[in] period Period duration [ms], must be > 0
258 * @param[in] min_sleep Minimal sleep duration [ms], must be < period
259 *
260 * @returns WaitRes::WOKEN if the thread has been woken up, WaitRes::TIMEOUT if the timeout has expired or period
261 * was 0
262 */
263 WaitRes SleepUntil(const uint32_t period, const uint32_t min_sleep = 0);
264
265 /**
266 * @brief Check if we should abort
267 */
269
270 //@}
271
272 // -----------------------------------------------------------------------------------------------------------------
273
274 private:
275 // clang-format off
276 std::string name_; //!< Thread name
277 std::unique_ptr<std::thread> thread_; //!< Thread handle
278 ThreadFunc func_; //!< Thread function
279 void *arg_; //!< Thread function argument
280 PrepFunc prep_; //!< Thread prepare function
281 CleanFunc clean_; //!< Thread cleanup function
282 std::atomic<bool> abort_ = false; //!< Abort signal
283 std::atomic<bool> started_ = false; //!< Thread was started
284 std::atomic<Status> status_ = Status::STOPPED; //!< Thread status
285 BinarySemaphore sem_; //!< Semaphore
286 // clang-format on
287 void _Thread(const bool try_catch); //!< Wrapper function to call the user thread function
288};
289
290// ---------------------------------------------------------------------------------------------------------------------
291
292/**
293 * @brief Set thread name
294 *
295 * Sets the thread name, which shows in htop etc.
296 *
297 * @param[in] name The thread name (1-6 characters, longer \c name is clipped at 6 chars)
298 */
299void SetThreadName(const std::string& name);
300
301/**
302 * @brief Get numeric thread ID
303 *
304 * Like std::this_thread::get_id(), but numeric (and therefore printf()-able, etc.).
305 */
306std::size_t ThisThreadId();
307
308/* ****************************************************************************************************************** */
309} // namespace thread
310} // namespace common
311} // namespace fpsdk
312#endif // __FPSDK_COMMON_THREAD_HPP__
A binary semaphore, useful for thread synchronisation.
Definition thread.hpp:59
WaitRes WaitUntil(const uint32_t period, const uint32_t min_sleep=0)
Wait (take), with timout aligned to a period.
WaitRes WaitFor(const uint32_t millis)
Wait (take), with timeout.
void Notify()
Notify ("signal", "give")
Helper class for handling threads.
Definition thread.hpp:155
Status GetStatus() const
Check thread status.
Thread(const std::string &name, ThreadFunc func, void *arg=nullptr, PrepFunc prep=nullptr, CleanFunc clean=nullptr)
Constructor.
WaitRes SleepUntil(const uint32_t period, const uint32_t min_sleep=0)
Sleep until next period start or woken up.
std::function< void(void *)> PrepFunc
Thread prepare function.
Definition thread.hpp:158
bool ShouldAbort()
Check if we should abort.
void Wakeup()
Wakup a sleeping thread.
std::function< void(void *)> CleanFunc
Thread cleanup function.
Definition thread.hpp:159
@ RUNNING
Running (Start(ed) and happily running)
Definition thread.hpp:223
@ FAILED
Failed (was Start()ed, but crashed due to an exception)
Definition thread.hpp:224
@ STOPPED
Stopped (not Start()ed, or properly and happily Stop()ped)
Definition thread.hpp:222
bool Stop()
Stop the thread.
~Thread()
Destructor, blocks until thread has stopped.
std::function< bool(Thread *, void *)> ThreadFunc
Thread main function.
Definition thread.hpp:157
WaitRes Sleep(const uint32_t millis)
Sleep until timeout or woken up.
const std::string & GetName()
Get thread name.
bool Start(const bool try_catch=true)
Start the thread.
WaitRes
Thread sleep result.
Definition thread.hpp:47
@ TIMEOUT
Sleep or wait timeout has expired (no wakeup, no interrupt)
@ WOKEN
Thread was woken up (sleep interrupted), or semaphore was taken (wait interrupted)
void SetThreadName(const std::string &name)
Set thread name.
std::size_t ThisThreadId()
Get numeric thread ID.
Fixposition SDK.