Fixposition SDK 0.0.0-heads/main-0-g4e80ed3
Collection of c++ libraries and apps for use with Fixposition products on Linux
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 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 * // We're only interested in the first ThreadFunc argument
117 * thread_ { "worker", std::bind(&Example::Worker, this, std::placeholders::_1) }
118 * { }
119 *
120 * void Run() {
121 *
122 * if (!thread_.Start()) {
123 * throw "...";
124 * }
125 *
126 * int n = 0;
127 * while (thread_.GetStatus() == Thread::Status::RUNNING) {
128 *
129 * sleep(500);
130 * thread_.Wakeup() // = BinarySemaphore::Notify()
131 *
132 * sleep(2000);
133 * thread_.Wakeup() // = BinarySemaphore::Notify()
134 *
135 * n++;
136 * if (n >= 10) {
137 * thread_.Stop();
138 * }
139 * }
140 * }
141 *
142 * private:
143 *
144 * // Note that the first argument is a reference to the thread itself.
145 * bool Worker(Thread& thread) {
146 * while (!thread.ShouldAbort()) {
147 * if (thread.Sleep(123) == WaitRes::WOKEN) { // = BinarySemaphore::SleepFor(1000)
148 * // We have been woken up
149 * } else {
150 * // Timeout expired
151 * }
152 * }
153 * return true;
154 * }
155 *
156 * Thread thread_;
157 * };
158 * @endcode
159 */
161{
162 public:
163 using ThreadFunc = std::function<bool(Thread&, void*)>; //!< Thread main function
164 using PrepFunc = std::function<void(void*)>; //!< Thread prepare function
165 using CleanFunc = std::function<void(void*)>; //!< Thread cleanup function
166
167 /**
168 * @brief Constructor
169 *
170 * @param[in] name Name of the thread, for debugging
171 * @param[in] func The thread function
172 * @param[in] arg Optional thread function user argument
173 * @param[in] prep Optional prepare function, called before the thread function
174 * @param[in] clean Optional cleanup function, called after the threadd stopped (or crashed)
175 *
176 * @note Constructing an instance delibarately does not automatically start the thread!
177 */
178 Thread(const std::string& name, ThreadFunc func, void* arg = nullptr, PrepFunc prep = nullptr,
179 CleanFunc clean = nullptr);
180
181 /**
182 * @brief Destructor, blocks until thread has stopped
183 */
185
186 // ----- Common methods -----
187
188 /**
189 * @brief Get thread name
190 *
191 * @returns the thread name
192 */
193 const std::string& GetName();
194
195 // -----------------------------------------------------------------------------------------------------------------
196
197 /**
198 * @name Main (controlling) thread methods
199 * @{
200 */
201
202 /**
203 * @brief Start the thread
204 *
205 * @param[in] try_catch Run user-supplied thread function in a try ... catch block
206 *
207 * @returns true if the thread was started, false otherwise
208 */
209 bool Start(const bool try_catch = true);
210
211 /**
212 * @brief Stop the thread
213 *
214 * @returns true if the thread was stopped, false otherwise
215 */
216 bool Stop();
217
218 /**
219 * @brief Wakup a sleeping thread
220 */
221 void Wakeup();
222
223 /**
224 * @brief Thread status
225 */
227 {
228 STOPPED, //!< Stopped (not Start()ed, or properly and happily Stop()ped)
229 RUNNING, //!< Running (Start(ed) and happily running)
230 FAILED, //!< Failed (was Start()ed, but crashed due to an exception)
231 };
232
233 /**
234 * @brief Check thread status
235 *
236 * @returns the thread status
237 */
239
240 //@}
241
242 // -----------------------------------------------------------------------------------------------------------------
243
244 /**
245 * @name Worker thread methods
246 * @{
247 */
248
249 /**
250 * @brief Sleep until timeout or woken up
251 *
252 * @param[in] millis Number of [ms] to sleep
253 *
254 * @returns WaitRes::WOKEN if the thread has been woken up, WaitRes::TIMEOUT if the timeout has expired
255 */
256 WaitRes Sleep(const uint32_t millis);
257
258 /**
259 * @brief Sleep until next period start or woken up
260 *
261 * See BinarySemaphore::WaitUntil() for a detailed explanation.
262 *
263 * @param[in] period Period duration [ms], must be > 0
264 * @param[in] min_sleep Minimal sleep duration [ms], must be < period
265 *
266 * @returns WaitRes::WOKEN if the thread has been woken up, WaitRes::TIMEOUT if the timeout has expired or period
267 * was 0
268 */
269 WaitRes SleepUntil(const uint32_t period, const uint32_t min_sleep = 0);
270
271 /**
272 * @brief Check if we should abort
273 */
275
276 //@}
277
278 // -----------------------------------------------------------------------------------------------------------------
279
280 private:
281 // clang-format off
282 std::string name_; //!< Thread name
283 std::unique_ptr<std::thread> thread_; //!< Thread handle
284 ThreadFunc func_; //!< Thread function
285 void *arg_; //!< Thread function argument
286 PrepFunc prep_; //!< Thread prepare function
287 CleanFunc clean_; //!< Thread cleanup function
288 std::atomic<bool> abort_ = false; //!< Abort signal
289 std::atomic<bool> started_ = false; //!< Thread was started
290 std::atomic<Status> status_ = Status::STOPPED; //!< Thread status
291 BinarySemaphore sem_; //!< Semaphore
292 // clang-format on
293 void _Thread(const bool try_catch); //!< Wrapper function to call the user thread function
294};
295
296// ---------------------------------------------------------------------------------------------------------------------
297
298/**
299 * @brief Set thread name
300 *
301 * Sets the thread name, which shows in htop etc.
302 *
303 * @param[in] name The thread name (1-6 characters, longer \c name is clipped at 6 chars)
304 */
305void SetThreadName(const std::string& name);
306
307/**
308 * @brief Get numeric thread ID
309 *
310 * Like std::this_thread::get_id(), but numeric (and therefore printf()-able, etc.).
311 */
312std::size_t ThisThreadId();
313
314/* ****************************************************************************************************************** */
315} // namespace thread
316} // namespace common
317} // namespace fpsdk
318#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:161
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:164
bool ShouldAbort()
Check if we should abort.
void Wakeup()
Wakup a sleeping thread.
std::function< void(void *)> CleanFunc
Thread cleanup function.
Definition thread.hpp:165
@ RUNNING
Running (Start(ed) and happily running)
Definition thread.hpp:229
@ FAILED
Failed (was Start()ed, but crashed due to an exception)
Definition thread.hpp:230
@ STOPPED
Stopped (not Start()ed, or properly and happily Stop()ped)
Definition thread.hpp:228
bool Stop()
Stop the thread.
~Thread()
Destructor, blocks until thread has stopped.
std::function< bool(Thread &, void *)> ThreadFunc
Thread main function.
Definition thread.hpp:163
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.