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