Thread.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include "anki/util/StdTypes.h"
  7. #include "anki/util/Array.h"
  8. #include "anki/util/NonCopyable.h"
  9. #include <atomic>
  10. #define ANKI_DISABLE_THREADPOOL_THREADING 0
  11. namespace anki {
  12. // Forward
  13. class ThreadPool;
  14. /// @addtogroup util_thread
  15. /// @{
  16. /// Thread implementation
  17. class Thread: public NonCopyable
  18. {
  19. public:
  20. using Id = U64;
  21. /// It holds some information to be passed to the thread's callback
  22. class Info
  23. {
  24. public:
  25. void* m_userData;
  26. const char* m_threadName;
  27. };
  28. /// The type of the tread callback
  29. using Callback = Error(*)(Info&);
  30. /// Create a thread with or without a name
  31. /// @param[in] name The name of the new thread. Can be nullptr
  32. Thread(const char* name);
  33. ~Thread();
  34. /// Start the thread
  35. /// @param userData The user data of the thread callback
  36. /// @param callback The thread callback that will be executed
  37. void start(void* userData, Callback callback);
  38. /// Wait for the thread to finish
  39. /// @return The error code of the thread's callback
  40. ANKI_USE_RESULT Error join();
  41. /// Identify the current thread
  42. static Id getCurrentThreadId();
  43. /// @privatesection
  44. /// @{
  45. const char* _getName() const
  46. {
  47. return &m_name[0];
  48. }
  49. void* _getUserData() const
  50. {
  51. return m_userData;
  52. }
  53. Callback _getCallback() const
  54. {
  55. return m_callback;
  56. }
  57. /// @}
  58. private:
  59. void* m_impl = nullptr; ///< The system native type
  60. Array<char, 32> m_name; ///< The name of the thread
  61. Callback m_callback = nullptr; ///< The callback
  62. void* m_userData = nullptr; ///< The user date to pass to the callback
  63. #if ANKI_ASSERTIONS
  64. Bool8 m_started = false;
  65. #endif
  66. };
  67. /// Mutex
  68. class Mutex: public NonCopyable
  69. {
  70. friend class ConditionVariable;
  71. public:
  72. Mutex();
  73. ~Mutex();
  74. /// Lock
  75. void lock();
  76. /// Try lock
  77. /// @return True if it was locked successfully
  78. Bool tryLock();
  79. /// Unlock
  80. void unlock();
  81. private:
  82. void* m_impl = nullptr; ///< The system native type
  83. };
  84. /// Condition variable
  85. class ConditionVariable: public NonCopyable
  86. {
  87. public:
  88. ConditionVariable();
  89. ~ConditionVariable();
  90. /// Signal one thread
  91. void notifyOne();
  92. /// Signal all threads
  93. void notifyAll();
  94. /// Bock until signaled.
  95. /// @param mtx The mutex.
  96. void wait(Mutex& mtx);
  97. private:
  98. void* m_impl = nullptr; ///< The system native type
  99. };
  100. /// Spin lock. Good if the critical section will be executed in a short period
  101. /// of time
  102. class SpinLock: public NonCopyable
  103. {
  104. public:
  105. /// Lock
  106. void lock()
  107. {
  108. while(m_lock.test_and_set(std::memory_order_acquire))
  109. {}
  110. }
  111. /// Unlock
  112. void unlock()
  113. {
  114. m_lock.clear(std::memory_order_release);
  115. }
  116. private:
  117. std::atomic_flag m_lock = ATOMIC_FLAG_INIT;
  118. };
  119. /// Lock guard. When constructed it locks a TMutex and unlocks it when it gets
  120. /// destroyed.
  121. template<typename TMutex>
  122. class LockGuard
  123. {
  124. public:
  125. LockGuard(TMutex& mtx)
  126. : m_mtx(&mtx)
  127. {
  128. m_mtx->lock();
  129. }
  130. ~LockGuard()
  131. {
  132. m_mtx->unlock();
  133. }
  134. private:
  135. TMutex* m_mtx;
  136. };
  137. /// A barrier for thread synchronization. It works almost like boost::barrier
  138. class Barrier: public NonCopyable
  139. {
  140. public:
  141. Barrier(U32 count);
  142. ~Barrier();
  143. /// Wait until all threads call wait().
  144. Bool wait();
  145. private:
  146. void* m_impl = nullptr;
  147. };
  148. // Forward
  149. namespace detail {
  150. class ThreadPoolThread;
  151. }
  152. /// Parallel task dispatcher. You feed it with tasks and sends them for
  153. /// execution in parallel and then waits for all to finish
  154. class ThreadPool: public NonCopyable
  155. {
  156. friend class detail::ThreadPoolThread;
  157. public:
  158. static constexpr U MAX_THREADS = 32; ///< An absolute limit
  159. /// A task assignment for a ThreadPool
  160. class Task
  161. {
  162. public:
  163. virtual ~Task()
  164. {}
  165. virtual Error operator()(U32 taskId, PtrSize threadsCount) = 0;
  166. /// Chose a starting and end index
  167. static void choseStartEnd(U32 taskId, PtrSize threadsCount,
  168. PtrSize elementsCount, PtrSize& start, PtrSize& end)
  169. {
  170. F32 tid = taskId;
  171. F32 div = F32(elementsCount) / threadsCount;
  172. start = PtrSize(tid * div);
  173. end = PtrSize((tid + 1.0) * div);
  174. }
  175. };
  176. /// Constructor
  177. ThreadPool(U32 threadsCount);
  178. ~ThreadPool();
  179. /// Assign a task to a working thread
  180. /// @param slot The slot of the task
  181. /// @param task The task. If it's nullptr then a dummy task will be assigned
  182. void assignNewTask(U32 slot, Task* task);
  183. /// Wait for all tasks to finish.
  184. /// @return The error code in one of the worker threads.
  185. ANKI_USE_RESULT Error waitForAllThreadsToFinish()
  186. {
  187. #if !ANKI_DISABLE_THREADPOOL_THREADING
  188. m_barrier.wait();
  189. #endif
  190. Error err = m_err;
  191. m_err = ErrorCode::NONE;
  192. return err;
  193. }
  194. PtrSize getThreadsCount() const
  195. {
  196. return m_threadsCount;
  197. }
  198. private:
  199. /// A dummy task for a ThreadPool
  200. class DummyTask: public Task
  201. {
  202. public:
  203. Error operator()(U32 taskId, PtrSize threadsCount)
  204. {
  205. (void)taskId;
  206. (void)threadsCount;
  207. return ErrorCode::NONE;
  208. }
  209. };
  210. #if !ANKI_DISABLE_THREADPOOL_THREADING
  211. Barrier m_barrier; ///< Synchronization barrier
  212. detail::ThreadPoolThread* m_threads = nullptr; ///< Threads array
  213. #endif
  214. U8 m_threadsCount = 0;
  215. Error m_err = ErrorCode::NONE;
  216. static DummyTask m_dummyTask;
  217. };
  218. /// @}
  219. } // end namespace anki