lzham_win32_threading.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. // File: lzham_task_pool_win32.h
  2. // See Copyright Notice and license at the end of include/lzham.h
  3. #pragma once
  4. #if LZHAM_USE_WIN32_API
  5. #if LZHAM_NO_ATOMICS
  6. #error No atomic operations defined in lzham_platform.h!
  7. #endif
  8. namespace lzham
  9. {
  10. class semaphore
  11. {
  12. LZHAM_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
  13. public:
  14. semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL)
  15. {
  16. m_handle = CreateSemaphoreA(NULL, initialCount, maximumCount, pName);
  17. if (NULL == m_handle)
  18. {
  19. LZHAM_FAIL("semaphore: CreateSemaphore() failed");
  20. }
  21. }
  22. ~semaphore()
  23. {
  24. if (m_handle)
  25. {
  26. CloseHandle(m_handle);
  27. m_handle = NULL;
  28. }
  29. }
  30. inline HANDLE get_handle(void) const { return m_handle; }
  31. void release(long releaseCount = 1)
  32. {
  33. if (0 == ReleaseSemaphore(m_handle, releaseCount, NULL))
  34. {
  35. LZHAM_FAIL("semaphore: ReleaseSemaphore() failed");
  36. }
  37. }
  38. bool wait(uint32 milliseconds = UINT32_MAX)
  39. {
  40. LZHAM_ASSUME(INFINITE == UINT32_MAX);
  41. DWORD result = WaitForSingleObject(m_handle, milliseconds);
  42. if (WAIT_FAILED == result)
  43. {
  44. LZHAM_FAIL("semaphore: WaitForSingleObject() failed");
  45. }
  46. return WAIT_OBJECT_0 == result;
  47. }
  48. private:
  49. HANDLE m_handle;
  50. };
  51. template<typename T>
  52. class tsstack
  53. {
  54. public:
  55. inline tsstack(bool use_freelist = true) :
  56. m_use_freelist(use_freelist)
  57. {
  58. LZHAM_VERIFY(((ptr_bits_t)this & (LZHAM_GET_ALIGNMENT(tsstack) - 1)) == 0);
  59. InitializeSListHead(&m_stack_head);
  60. InitializeSListHead(&m_freelist_head);
  61. }
  62. inline ~tsstack()
  63. {
  64. clear();
  65. }
  66. inline void clear()
  67. {
  68. for ( ; ; )
  69. {
  70. node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
  71. if (!pNode)
  72. break;
  73. LZHAM_MEMORY_IMPORT_BARRIER
  74. helpers::destruct(&pNode->m_obj);
  75. lzham_free(pNode);
  76. }
  77. flush_freelist();
  78. }
  79. inline void flush_freelist()
  80. {
  81. if (!m_use_freelist)
  82. return;
  83. for ( ; ; )
  84. {
  85. node* pNode = (node*)InterlockedPopEntrySList(&m_freelist_head);
  86. if (!pNode)
  87. break;
  88. LZHAM_MEMORY_IMPORT_BARRIER
  89. lzham_free(pNode);
  90. }
  91. }
  92. inline bool try_push(const T& obj)
  93. {
  94. node* pNode = alloc_node();
  95. if (!pNode)
  96. return false;
  97. helpers::construct(&pNode->m_obj, obj);
  98. LZHAM_MEMORY_EXPORT_BARRIER
  99. InterlockedPushEntrySList(&m_stack_head, &pNode->m_slist_entry);
  100. return true;
  101. }
  102. inline bool pop(T& obj)
  103. {
  104. node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
  105. if (!pNode)
  106. return false;
  107. LZHAM_MEMORY_IMPORT_BARRIER
  108. obj = pNode->m_obj;
  109. helpers::destruct(&pNode->m_obj);
  110. free_node(pNode);
  111. return true;
  112. }
  113. private:
  114. SLIST_HEADER m_stack_head;
  115. SLIST_HEADER m_freelist_head;
  116. struct node
  117. {
  118. SLIST_ENTRY m_slist_entry;
  119. T m_obj;
  120. };
  121. bool m_use_freelist;
  122. inline node* alloc_node()
  123. {
  124. node* pNode = m_use_freelist ? (node*)InterlockedPopEntrySList(&m_freelist_head) : NULL;
  125. if (!pNode)
  126. pNode = (node*)lzham_malloc(sizeof(node));
  127. return pNode;
  128. }
  129. inline void free_node(node* pNode)
  130. {
  131. if (m_use_freelist)
  132. InterlockedPushEntrySList(&m_freelist_head, &pNode->m_slist_entry);
  133. else
  134. lzham_free(pNode);
  135. }
  136. };
  137. class task_pool
  138. {
  139. public:
  140. task_pool();
  141. task_pool(uint num_threads);
  142. ~task_pool();
  143. enum { cMaxThreads = LZHAM_MAX_HELPER_THREADS };
  144. bool init(uint num_threads);
  145. void deinit();
  146. inline uint get_num_threads() const { return m_num_threads; }
  147. inline uint get_num_outstanding_tasks() const { return m_num_outstanding_tasks; }
  148. // C-style task callback
  149. typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
  150. bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL);
  151. class executable_task
  152. {
  153. public:
  154. virtual void execute_task(uint64 data, void* pData_ptr) = 0;
  155. };
  156. // It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
  157. bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL);
  158. template<typename S, typename T>
  159. inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL);
  160. template<typename S, typename T>
  161. inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL);
  162. void join();
  163. private:
  164. struct task
  165. {
  166. //inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
  167. uint64 m_data;
  168. void* m_pData_ptr;
  169. union
  170. {
  171. task_callback_func m_callback;
  172. executable_task* m_pObj;
  173. };
  174. uint m_flags;
  175. };
  176. tsstack<task> m_task_stack;
  177. uint m_num_threads;
  178. HANDLE m_threads[cMaxThreads];
  179. semaphore m_tasks_available;
  180. enum task_flags
  181. {
  182. cTaskFlagObject = 1
  183. };
  184. volatile atomic32_t m_num_outstanding_tasks;
  185. volatile atomic32_t m_exit_flag;
  186. void process_task(task& tsk);
  187. static unsigned __stdcall thread_func(void* pContext);
  188. };
  189. enum object_task_flags
  190. {
  191. cObjectTaskFlagDefault = 0,
  192. cObjectTaskFlagDeleteAfterExecution = 1
  193. };
  194. template<typename T>
  195. class object_task : public task_pool::executable_task
  196. {
  197. public:
  198. object_task(uint flags = cObjectTaskFlagDefault) :
  199. m_pObject(NULL),
  200. m_pMethod(NULL),
  201. m_flags(flags)
  202. {
  203. }
  204. typedef void (T::*object_method_ptr)(uint64 data, void* pData_ptr);
  205. object_task(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault) :
  206. m_pObject(pObject),
  207. m_pMethod(pMethod),
  208. m_flags(flags)
  209. {
  210. LZHAM_ASSERT(pObject && pMethod);
  211. }
  212. void init(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
  213. {
  214. LZHAM_ASSERT(pObject && pMethod);
  215. m_pObject = pObject;
  216. m_pMethod = pMethod;
  217. m_flags = flags;
  218. }
  219. T* get_object() const { return m_pObject; }
  220. object_method_ptr get_method() const { return m_pMethod; }
  221. virtual void execute_task(uint64 data, void* pData_ptr)
  222. {
  223. (m_pObject->*m_pMethod)(data, pData_ptr);
  224. if (m_flags & cObjectTaskFlagDeleteAfterExecution)
  225. lzham_delete(this);
  226. }
  227. protected:
  228. T* m_pObject;
  229. object_method_ptr m_pMethod;
  230. uint m_flags;
  231. };
  232. template<typename S, typename T>
  233. inline bool task_pool::queue_object_task(S* pObject, T pObject_method, uint64 data, void* pData_ptr)
  234. {
  235. object_task<S> *pTask = lzham_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
  236. if (!pTask)
  237. return false;
  238. return queue_task(pTask, data, pData_ptr);
  239. }
  240. template<typename S, typename T>
  241. inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
  242. {
  243. LZHAM_ASSERT(m_num_threads);
  244. LZHAM_ASSERT(pObject);
  245. LZHAM_ASSERT(num_tasks);
  246. if (!num_tasks)
  247. return true;
  248. bool status = true;
  249. uint i;
  250. for (i = 0; i < num_tasks; i++)
  251. {
  252. task tsk;
  253. tsk.m_pObj = lzham_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
  254. if (!tsk.m_pObj)
  255. {
  256. status = false;
  257. break;
  258. }
  259. tsk.m_data = first_data + i;
  260. tsk.m_pData_ptr = pData_ptr;
  261. tsk.m_flags = cTaskFlagObject;
  262. if (!m_task_stack.try_push(tsk))
  263. {
  264. status = false;
  265. break;
  266. }
  267. }
  268. if (i)
  269. {
  270. atomic_add32(&m_num_outstanding_tasks, i);
  271. m_tasks_available.release(i);
  272. }
  273. return status;
  274. }
  275. inline void lzham_sleep(unsigned int milliseconds)
  276. {
  277. Sleep(milliseconds);
  278. }
  279. uint lzham_get_max_helper_threads();
  280. } // namespace lzham
  281. #endif // LZHAM_USE_WIN32_API