Ocean
Thread.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_BASE_THREAD_H
9 #define META_OCEAN_BASE_THREAD_H
10 
11 #include "ocean/base/Base.h"
12 #include "ocean/base/Lock.h"
13 #include "ocean/base/Timestamp.h"
14 #include "ocean/base/Triple.h"
15 
16 #if defined(_WINDOWS)
17  #include <winsock2.h>
18  #include <windows.h>
19 #else
20  #include <pthread.h>
21 #endif
22 
23 #include <atomic>
24 
25 namespace Ocean
26 {
27 
28 /**
29  * This class implements a thread.
30  * The implementation can be used in two ways:<br>
31  * First: Derive an own class from this thread class and overwrite the internal Thread::threadRun() function.<br>
32  * This function will then be called once the thread has been started. If this run function returns the thread will be closed.<br>
33  * Second: Set the run callback function which will be called instead of the normal internal run function.<br>
34  * Similar to the first solution the thread will be closed after the callback function returns.<br>
35  * However, if the normal internal run function is overloaded by a derived call a possible defined callback function has no effect.<br>
36  *
37  * Each thread can be started using the Thread::startThread() function,<br>
38  * and stopped using the Thread::stopThread() function.<br>
39  * However, the stop function will not explicit terminate the thread, it sets the thread's should-stop-state only.<br>
40  * Therefore, an implementation using this thread class must check the thread's should-stop-state recurrently.<br>
41  * Use Thread::shouldThreadStop() to determine whether your implementation should stop the thread execution.<br>
42  *
43  * If the thread execution does not return after a Thread::stopThread() the thread can be kill in an explicit manner.<br>
44  * Beware: Such kind of rough termination should be avoided and in most cases evidences a dirty usage of the internal run function.<br>
45  *
46  * See this tutorial:
47  * @code
48  * // Any class using the thread class as base class.
49  * class Timer : protected Thread
50  * {
51  * public:
52  *
53  * // Creates a new timer object.
54  * Timer();
55  *
56  * // Stars the timer.
57  * void start();
58  *
59  * // Stops the timer.
60  * void stop()
61  *
62  * private:
63  *
64  * // Internal thread run function which overloaded the function from the thread class.
65  * void threadRun() override;
66  * };
67  *
68  * Timer::Timer() :
69  * Thread("Optional thread name")
70  * {
71  * // nothing to do here
72  * // optional the thread could be started here,
73  * // however we use the start function for this explicitly
74  * }
75  *
76  * void Timer::start()
77  * {
78  * startThread();
79  * }
80  *
81  * void Timer::stop()
82  * {
83  * stopThread();
84  * }
85  *
86  * void Timer::threadRun()
87  * {
88  * // check whether this thread should stop execution
89  * while (shouldThreadStop() == false)
90  * {
91  * // do anything a timer can do
92  *
93  * // to avoid a very high CPU load it can be suitable to sleep this thread for a small time
94  * sleep(1);
95  * }
96  *
97  * // now the run function will return and the thread will be closed
98  * }
99  *
100  * // Usage of the timer in any environment, here it is used in a main function
101  * void main()
102  * {
103  * Timer timer;
104  * timer.start();
105  *
106  * // do anything in a e.g. message loop
107  *
108  * timer.stop();
109  * }
110  * @endcode
111  * @see ThreadPool, Scheduler, Worker.
112  * @ingroup base
113  */
114 class OCEAN_BASE_EXPORT Thread
115 {
116  public:
117 
118  /**
119  * This class implements a platform independent wrapper for thread ids.
120  */
121  class ThreadId
122  {
123  friend class Thread;
124 
125  public:
126 
127  /**
128  * Creates a new thread id object with invalid id value.
129  */
130  ThreadId() = default;
131 
132  /**
133  * Returns whether this object holds a valid id.
134  * @return True, if succeeded
135  */
136  inline bool isValid() const;
137 
138  /**
139  * Returns the hash value of this thread id.
140  * On platforms directly providing a unique integer thread id as value the hash value is exactly that integer value.
141  * @return The unique hash value of this thread id
142  */
143  inline uint64_t hash() const;
144 
145  /**
146  * Returns whether two thread id objects are identical.
147  * @param id The second thread id to compare
148  * @return True, if succeeded
149  */
150  inline bool operator==(const ThreadId& id) const;
151 
152  /**
153  * Returns whether two thread id objects are not identical.
154  * @param id The second thread id to compare
155  * @return True, if succeeded
156  */
157  inline bool operator!=(const ThreadId& id) const;
158 
159  /**
160  * Compares two thread id objects by their hash values.
161  * @param id The second thread id to compare
162  * @return True, if the hash value of this object is smaller than the hash value of the second object
163  */
164  inline bool operator<(const ThreadId& id) const;
165 
166  protected:
167 
168  /**
169  * Creates a new thread id object.
170  * @param value The value to be wrapped by the new object
171  */
172  explicit inline ThreadId(const uint64_t value);
173 
174  /**
175  * Returns an invalid thread id value.
176  * @return The invalid thread id value
177  */
178  static constexpr uint64_t invalidThreadId();
179 
180  protected:
181 
182  /// The value of the thread id.
183  uint64_t value_ = invalidThreadId();
184  };
185 
186  /**
187  * Definition of different thread priority values.
188  */
190  {
191  /// The thread runs if the system is idle.
193  /// The thread has a priority below normal.
195  /// The thread has a normal priority.
197  /// The thread has a priority above normal.
199  /// The thread has a high priority.
201  /// The thread has a real time priority.
202  PRIORTY_REALTIME
203  };
204 
205  protected:
206 
207 #if defined(_WINDOWS)
208 #else
209 
210  /**
211  * Definition of a pair holding a thread id and a boolean state.
212  */
213  typedef std::pair<pthread_t, bool> TimedJoinPair;
214 
215 #endif
216 
217  public:
218 
219  /**
220  * Creates a new thread object.
221  * The thread will be initialized with a seed value automatically generated by using RandomI::random32().
222  * @param name Optional thread name which can be helpful for debugging tasks
223  */
224  explicit Thread(const std::string& name = std::string());
225 
226  /**
227  * Creates a new thread object.
228  * @param randomNumberSeedValue An explicit seed value for the random number initialization, with range [0, infinity)
229  * @param name Optional thread name which can be helpful for debugging tasks
230  */
231  explicit Thread(const unsigned int randomNumberSeedValue, const std::string& name = std::string());
232 
233  /**
234  * Destructs a thread object.
235  */
236  virtual ~Thread();
237 
238  /**
239  * Starts the thread.
240  * @return True, if the thread is not active
241  */
242  bool startThread();
243 
244  /**
245  * Informs the thread to stop.
246  * see shouldThreadStop().
247  */
248  void stopThread();
249 
250  /**
251  * Terminates the thread.
252  * Beware: The thread will be terminated in a very rough way.
253  * @return True, if the thread could be terminated
254  */
256 
257  /**
258  * Waits until this thread has been stopped.
259  * @param timeout The number of milliseconds the caller thread will wait for this thread, -1 to wait infinite
260  * @return True, if the thread has finished; False, if the timeout was exceeded
261  */
262  bool joinThread(const unsigned int timeout = (unsigned int)(-1));
263 
264  /**
265  * Returns whether this thread should stop.
266  * @return True, if so
267  * @see stopThread().
268  */
269  bool shouldThreadStop() const;
270 
271  /**
272  * Returns whether this thread has been invoked to start immediately.
273  * Beware: No information is provided whether the thread is active already.
274  * However, to not start a thread invoked to start again, instead wait for the termination.
275  * @return True, if so
276  */
278 
279  /**
280  * Returns whether this thread is active.
281  * An active thread currently executes the internal thread function.
282  * @return True, if so
283  */
284  bool isThreadActive() const;
285 
286  /**
287  * Sleeps the calling thread for a given time.
288  * @param ms Sleeping time in ms
289  */
290  static void sleep(unsigned int ms);
291 
292  /**
293  * Gives up the remaining thread time.
294  */
295  static void giveUp();
296 
297  /**
298  * Returns the thread id of the current (calling) thread.
299  * @return The thread id of the current thread
300  */
302 
303  /**
304  * Returns the priority of the current thread.
305  * @return Thread priority
306  */
308 
309  /**
310  * Sets the priority of the current thread.
311  * @param priority Thread priority to set
312  * @return True, if succeeded
313  */
314  static bool setThreadPriority(const ThreadPriority priority);
315 
316  /**
317  * Waits until an object/variable has an expected value.
318  * @param object Reference to the object/variable whose value is to be checked
319  * @param expectedValue The value that the object/variable is expected to have
320  * @param timeout The optional timeout of this function, in seconds, with range [0, infinity), -1 to wait forever
321  * @return True, if the object/variable had the expected value when this function returned; False, if a timeout was defined and the timeout exceeded
322  * @tparam TObject The data type of the object/variable to check
323  * @tparam TExpectedValue The data type of the expected value
324  */
325  template <typename TObject, typename TExpectedValue>
326  static bool waitForValue(TObject& object, const TExpectedValue& expectedValue, const double timeout = -1.0);
327 
328  /**
329  * Waits until an object/variable has an expected value.
330  * The provided temporary scoped lock will be repeatingly locked and released while the function is waiting and while accessing the object/variable.<br>
331  * The temporary scoped lock needs to be locked before calling this function, and it will also be locked when the function returns.
332  * @param object Reference to the object/variable whose value is to be checked
333  * @param expectedValue The value that the object/variable is expected to have
334  * @param temporaryScopedLock The temporary scoped lock which needs to be locked when calling the function; will be locked when the function returns
335  * @param timeout The optional timeout of this function, in seconds, with range [0, infinity), -1 to wait forever
336  * @return True, if the object/variable had the expected value when this function returned; False, if a timeout was defined and the timeout exceeded
337  * @tparam TObject The data type of the object/variable to check
338  * @tparam TExpectedValue The data type of the expected value
339  */
340  template <typename TObject, typename TExpectedValue>
341  static bool waitForValue(TObject& object, const TExpectedValue& expectedValue, TemporaryScopedLock& temporaryScopedLock, const double timeout = -1.0);
342 
343 #ifdef __APPLE__
344 
345  /**
346  * Implements a thread join function with timeout value.
347  * Depending on the platform, this function may not exist in the default libraries (e.g., on Apple platforms).
348  * @param thread The thread for which the ending function will wait
349  * @param retval The optional return value of the thread
350  * @param abstime The absolute timestamp after which the thread will have been ended
351  * @return Zero (0), if succeeded
352  */
353  static int pthread_timedjoin_np(pthread_t thread, void** retval, const struct timespec* abstime);
354 
355 #endif
356 
357  protected:
358 
359  /**
360  * Disabled copy constructor.
361  * @param thread The object that would be copied
362  */
363  Thread(const Thread& thread) = delete;
364 
365  /**
366  * Creates the thread itself.
367  */
368  void createThread();
369 
370  /**
371  * Destroys the thread itself.
372  * However the thread must be terminated before!
373  */
375 
376  /**
377  * Tries to stop the thread gracefully.
378  * However, if the thread can not be stopped it is terminated in a rough manner.<br>
379  * Call this function in the destructor of a derived class.
380  * @param timeout Time to wait for a graceful thread termination, in ms
381  */
382  void stopThreadExplicitly(const unsigned int timeout = 5000u);
383 
384  /**
385  * This function has to be overloaded in derived class.
386  */
387  virtual void threadRun() = 0;
388 
389  /**
390  * The disabled assign operator.
391  * @param thread The object that would be assigned
392  */
393  Thread& operator=(const Thread& thread) = delete;
394 
395  private:
396 
397 #ifdef __APPLE__
398 
399  /**
400  * The helper function for the pthread_timedjoin_np() implementation.
401  * @param threadData The thread's data
402  * @return The return value of the thread
403  */
404  static void* pthread_timedjoin_np_helper(void* threadData);
405 
406 #endif
407 
408  /**
409  * Platform independent internal thread function calling the external thread function.
410  */
412 
413 #if defined(_WINDOWS)
414 
415  /**
416  * Internal thread function calling the external thread function.
417  * @param data The data object which will contain the thread owner object
418  * @return Thread return value
419  */
420  static DWORD __stdcall staticThreadRun(void* data);
421 
422 #else
423 
424  /**
425  * Internal thread function calling the external thread function.
426  * @param data The data object which will contain the thread owner object
427  */
428  static void* staticThreadRun(void* data);
429 
430 #endif
431 
432  private:
433 
434 #if defined(_WINDOWS)
435 
436  /// Internal windows thread handle.
437  HANDLE threadHandle_ = nullptr;
438 
439 #else
440 
441  /// Internal pthread object.
442  pthread_t threadObject_ = 0;
443 
444 #endif
445 
446  /// Determines whether this thread should stop.
447  std::atomic<bool> threadShouldStop_{false};
448 
449  /// Determines whether this thread is actually running.
450  bool threadIsActive_ = false;
451 
452  /// Determines whether this thread has been invoked to start immediately.
453  bool threadIsInvokedToStart_ = false;
454 
455  /// Name of the thread.
456  std::string threadName_;
457 
458  /// The seed value for random number generators.
459  unsigned int threadRandomNumberSeedValue_ = 0u;
460 };
461 
462 inline Thread::ThreadId::ThreadId(const uint64_t value) :
463  value_(value)
464 {
465  // nothing to do here
466 }
467 
468 inline bool Thread::ThreadId::isValid() const
469 {
470  return value_ != invalidThreadId();
471 }
472 
473 inline uint64_t Thread::ThreadId::hash() const
474 {
475  return value_;
476 }
477 
478 inline bool Thread::ThreadId::operator==(const ThreadId& id) const
479 {
480  return value_ == id.value_;
481 }
482 
483 inline bool Thread::ThreadId::operator!=(const ThreadId& id) const
484 {
485  return !(*this == id);
486 }
487 
488 inline bool Thread::ThreadId::operator<(const ThreadId& id) const
489 {
490  return hash() < id.hash();
491 }
492 
494 {
495  return uint64_t(-1);
496 }
497 
498 template <typename TObject, typename TExpectedValue>
499 bool Thread::waitForValue(TObject& object, const TExpectedValue& expectedValue, const double timeout)
500 {
501  const Timestamp startTimestamp(timeout < 0.0 ? false : true);
502 
503  while (true)
504  {
505  if (object == expectedValue)
506  {
507  return true;
508  }
509 
510  if (timeout >= 0.0 && startTimestamp.hasTimePassed(timeout))
511  {
512  return false;
513  }
514 
515  // let's sleep for 1ms
516 
517  sleep(1u);
518  }
519 }
520 
521 template <typename TObject, typename TExpectedValue>
522 bool Thread::waitForValue(TObject& object, const TExpectedValue& expectedValue, TemporaryScopedLock& temporaryScopedLock, const double timeout)
523 {
524  ocean_assert(!temporaryScopedLock.isReleased());
525 
526  Lock* lock = temporaryScopedLock.lock();
527  ocean_assert(lock != nullptr);
528 
529  if (object == expectedValue)
530  {
531  return true;
532  }
533 
534  temporaryScopedLock.release();
535 
536  const Timestamp startTimestamp(timeout < 0.0 ? false : true);
537 
538  while (true)
539  {
540  temporaryScopedLock.relock(*lock);
541 
542  if (object == expectedValue)
543  {
544  return true;
545  }
546 
547  if (timeout >= 0.0 && startTimestamp.hasTimePassed(timeout))
548  {
549  return false;
550  }
551 
552  temporaryScopedLock.release();
553 
554  // let's sleep for 1ms
555 
556  sleep(1u);
557  }
558 }
559 
560 }
561 
562 #endif // META_OCEAN_BASE_THREAD_H
This class implements a recursive lock object.
Definition: Lock.h:31
This class implements a recursive scoped lock object allowing to release the lock before the scoped o...
Definition: Lock.h:254
void relock(Lock &lock)
Re-locks this scoped lock with a given lock.
Definition: Lock.h:525
Lock * lock() const
Returns the lock object which (if existing) is locked during the existence of this scoped lock object...
Definition: Lock.h:509
void release()
Explicitly releases the lock before the scoped lock object is released.
Definition: Lock.h:514
bool isReleased() const
Returns whether this scoped lock is released already.
Definition: Lock.h:536
This class implements a platform independent wrapper for thread ids.
Definition: Thread.h:122
uint64_t hash() const
Returns the hash value of this thread id.
Definition: Thread.h:473
bool isValid() const
Returns whether this object holds a valid id.
Definition: Thread.h:468
ThreadId()=default
Creates a new thread id object with invalid id value.
static constexpr uint64_t invalidThreadId()
Returns an invalid thread id value.
Definition: Thread.h:493
bool operator!=(const ThreadId &id) const
Returns whether two thread id objects are not identical.
Definition: Thread.h:483
bool operator<(const ThreadId &id) const
Compares two thread id objects by their hash values.
Definition: Thread.h:488
bool operator==(const ThreadId &id) const
Returns whether two thread id objects are identical.
Definition: Thread.h:478
This class implements a thread.
Definition: Thread.h:115
static ThreadId currentThreadId()
Returns the thread id of the current (calling) thread.
void createThread()
Creates the thread itself.
static void * staticThreadRun(void *data)
Internal thread function calling the external thread function.
bool startThread()
Starts the thread.
static bool setThreadPriority(const ThreadPriority priority)
Sets the priority of the current thread.
bool isThreadInvokedToStart() const
Returns whether this thread has been invoked to start immediately.
void stopThread()
Informs the thread to stop.
std::string threadName_
Name of the thread.
Definition: Thread.h:456
Thread & operator=(const Thread &thread)=delete
The disabled assign operator.
void destroyThread()
Destroys the thread itself.
bool terminateThread()
Terminates the thread.
Thread(const Thread &thread)=delete
Disabled copy constructor.
static int pthread_timedjoin_np(pthread_t thread, void **retval, const struct timespec *abstime)
Implements a thread join function with timeout value.
ThreadPriority
Definition of different thread priority values.
Definition: Thread.h:190
@ PRIORITY_IDLE
The thread runs if the system is idle.
Definition: Thread.h:192
@ PRIORTY_BELOW_NORMAL
The thread has a priority below normal.
Definition: Thread.h:194
@ PRIORTY_NORMAL
The thread has a normal priority.
Definition: Thread.h:196
@ PRIORTY_ABOVE_NORMAL
The thread has a priority above normal.
Definition: Thread.h:198
@ PRIORTY_HIGH
The thread has a high priority.
Definition: Thread.h:200
static void sleep(unsigned int ms)
Sleeps the calling thread for a given time.
virtual ~Thread()
Destructs a thread object.
void internalThreadRun()
Platform independent internal thread function calling the external thread function.
static void giveUp()
Gives up the remaining thread time.
Thread(const std::string &name=std::string())
Creates a new thread object.
bool joinThread(const unsigned int timeout=(unsigned int)(-1))
Waits until this thread has been stopped.
std::pair< pthread_t, bool > TimedJoinPair
Definition of a pair holding a thread id and a boolean state.
Definition: Thread.h:213
void stopThreadExplicitly(const unsigned int timeout=5000u)
Tries to stop the thread gracefully.
virtual void threadRun()=0
This function has to be overloaded in derived class.
static DWORD __stdcall staticThreadRun(void *data)
Internal thread function calling the external thread function.
static bool waitForValue(TObject &object, const TExpectedValue &expectedValue, const double timeout=-1.0)
Waits until an object/variable has an expected value.
Definition: Thread.h:499
bool isThreadActive() const
Returns whether this thread is active.
Thread(const unsigned int randomNumberSeedValue, const std::string &name=std::string())
Creates a new thread object.
static void * pthread_timedjoin_np_helper(void *threadData)
The helper function for the pthread_timedjoin_np() implementation.
bool shouldThreadStop() const
Returns whether this thread should stop.
static ThreadPriority threadPriority()
Returns the priority of the current thread.
This class implements a timestamp.
Definition: Timestamp.h:36
bool hasTimePassed(const double seconds, const Timestamp &currentTimestamp=Timestamp(true)) const
Returns whether a specified amount of time has passed since this timestamp.
Definition: Timestamp.h:290
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15