You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

338 lines
8.4 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
  5. // © 2014—2015 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
  6. //
  7. // This library is free software. It comes without any warranty, to
  8. // the extent permitted by applicable law. You can redistribute it
  9. // and/or modify it under the terms of the Do What the Fuck You Want
  10. // to Public License, Version 2, as published by the WTFPL Task Force.
  11. // See http://www.wtfpl.net/ for more details.
  12. //
  13. #pragma once
  14. //
  15. // The base classes for multithreading
  16. // -----------------------------------
  17. //
  18. #if defined HAVE_PTHREAD_H
  19. # include <pthread.h>
  20. #elif defined _XBOX
  21. # include <xtl.h>
  22. # undef near /* Fuck Microsoft */
  23. # undef far /* Fuck Microsoft again */
  24. #elif defined _WIN32
  25. # include <windows.h>
  26. # undef near /* Fuck Microsoft */
  27. # undef far /* Fuck Microsoft again */
  28. #else
  29. # error No threading support yet :(
  30. #endif
  31. namespace lol
  32. {
  33. class mutex_base
  34. {
  35. public:
  36. mutex_base()
  37. {
  38. #if defined HAVE_PTHREAD_H
  39. pthread_mutex_init(&m_mutex, nullptr);
  40. #elif defined _WIN32
  41. InitializeCriticalSection(&m_mutex);
  42. #endif
  43. }
  44. ~mutex_base()
  45. {
  46. #if defined HAVE_PTHREAD_H
  47. pthread_mutex_destroy(&m_mutex);
  48. #elif defined _WIN32
  49. DeleteCriticalSection(&m_mutex);
  50. #endif
  51. }
  52. void lock()
  53. {
  54. #if defined HAVE_PTHREAD_H
  55. pthread_mutex_lock(&m_mutex);
  56. #elif defined _WIN32
  57. EnterCriticalSection(&m_mutex);
  58. #endif
  59. }
  60. void unlock()
  61. {
  62. #if defined HAVE_PTHREAD_H
  63. pthread_mutex_unlock(&m_mutex);
  64. #elif defined _WIN32
  65. LeaveCriticalSection(&m_mutex);
  66. #endif
  67. }
  68. private:
  69. #if defined HAVE_PTHREAD_H
  70. pthread_mutex_t m_mutex;
  71. #elif defined _WIN32
  72. CRITICAL_SECTION m_mutex;
  73. #endif
  74. };
  75. template<typename T, int N>
  76. class queue_base
  77. {
  78. public:
  79. queue_base()
  80. {
  81. m_start = m_count = 0;
  82. #if defined HAVE_PTHREAD_H
  83. m_poppers = m_pushers = 0;
  84. pthread_mutex_init(&m_mutex, nullptr);
  85. pthread_cond_init(&m_empty_cond, nullptr);
  86. pthread_cond_init(&m_full_cond, nullptr);
  87. #elif defined _WIN32
  88. m_empty_sem = CreateSemaphore(nullptr, CAPACITY, CAPACITY, nullptr);
  89. m_full_sem = CreateSemaphore(nullptr, 0, CAPACITY, nullptr);
  90. InitializeCriticalSection(&m_mutex);
  91. #endif
  92. }
  93. ~queue_base()
  94. {
  95. #if defined HAVE_PTHREAD_H
  96. pthread_cond_destroy(&m_empty_cond);
  97. pthread_cond_destroy(&m_full_cond);
  98. pthread_mutex_destroy(&m_mutex);
  99. #elif defined _WIN32
  100. CloseHandle(m_empty_sem);
  101. CloseHandle(m_full_sem);
  102. DeleteCriticalSection(&m_mutex);
  103. #endif
  104. }
  105. ptrdiff_t count()
  106. {
  107. ptrdiff_t current_count = 0;
  108. #if defined HAVE_PTHREAD_H
  109. pthread_mutex_lock(&m_mutex);
  110. /* If queue is full, wait on the "full" cond var. */
  111. m_pushers++;
  112. while (m_count == CAPACITY)
  113. pthread_cond_wait(&m_full_cond, &m_mutex);
  114. m_pushers--;
  115. #elif defined _WIN32
  116. WaitForSingleObject(m_empty_sem, INFINITE);
  117. EnterCriticalSection(&m_mutex);
  118. #endif
  119. current_count = (ptrdiff_t)m_count;
  120. #if defined HAVE_PTHREAD_H
  121. /* If there were poppers waiting, signal the "empty" cond var. */
  122. if (m_poppers)
  123. pthread_cond_signal(&m_empty_cond);
  124. pthread_mutex_unlock(&m_mutex);
  125. #elif defined _WIN32
  126. LeaveCriticalSection(&m_mutex);
  127. ReleaseSemaphore(m_full_sem, 1, nullptr);
  128. #endif
  129. return current_count;
  130. }
  131. void push(T value)
  132. {
  133. #if defined HAVE_PTHREAD_H
  134. pthread_mutex_lock(&m_mutex);
  135. /* If queue is full, wait on the "full" cond var. */
  136. m_pushers++;
  137. while (m_count == CAPACITY)
  138. pthread_cond_wait(&m_full_cond, &m_mutex);
  139. m_pushers--;
  140. #elif defined _WIN32
  141. WaitForSingleObject(m_empty_sem, INFINITE);
  142. EnterCriticalSection(&m_mutex);
  143. #endif
  144. /* Push value */
  145. m_values[(m_start + m_count) % CAPACITY] = value;
  146. m_count++;
  147. #if defined HAVE_PTHREAD_H
  148. /* If there were poppers waiting, signal the "empty" cond var. */
  149. if (m_poppers)
  150. pthread_cond_signal(&m_empty_cond);
  151. pthread_mutex_unlock(&m_mutex);
  152. #elif defined _WIN32
  153. LeaveCriticalSection(&m_mutex);
  154. ReleaseSemaphore(m_full_sem, 1, nullptr);
  155. #endif
  156. }
  157. bool try_push(T value)
  158. {
  159. #if defined HAVE_PTHREAD_H
  160. pthread_mutex_lock(&m_mutex);
  161. /* If queue is full, wait on the "full" cond var. */
  162. if (m_count == CAPACITY)
  163. {
  164. pthread_mutex_unlock(&m_mutex);
  165. return false;
  166. }
  167. #elif defined _WIN32
  168. DWORD status = WaitForSingleObject(m_empty_sem, 0);
  169. if (status == WAIT_TIMEOUT)
  170. return false;
  171. EnterCriticalSection(&m_mutex);
  172. #endif
  173. /* Push value */
  174. m_values[(m_start + m_count) % CAPACITY] = value;
  175. m_count++;
  176. #if defined HAVE_PTHREAD_H
  177. /* If there were poppers waiting, signal the "empty" cond var. */
  178. if (m_poppers)
  179. pthread_cond_signal(&m_empty_cond);
  180. pthread_mutex_unlock(&m_mutex);
  181. #elif defined _WIN32
  182. LeaveCriticalSection(&m_mutex);
  183. ReleaseSemaphore(m_full_sem, 1, nullptr);
  184. #endif
  185. return true;
  186. }
  187. T pop()
  188. {
  189. #if defined HAVE_PTHREAD_H
  190. pthread_mutex_lock(&m_mutex);
  191. /* Wait until there is something in the queue. Be careful, we
  192. * could get woken up but another thread may have eaten the
  193. * message in the meantime. */
  194. m_poppers++;
  195. while (m_count == 0)
  196. pthread_cond_wait(&m_empty_cond, &m_mutex);
  197. m_poppers--;
  198. #elif defined _WIN32
  199. WaitForSingleObject(m_full_sem, INFINITE);
  200. EnterCriticalSection(&m_mutex);
  201. #endif
  202. /* Pop value */
  203. T ret = m_values[m_start];
  204. m_start = (m_start + 1) % CAPACITY;
  205. m_count--;
  206. #if defined HAVE_PTHREAD_H
  207. /* If there were pushers waiting, signal the "full" cond var. */
  208. if (m_pushers)
  209. pthread_cond_signal(&m_full_cond);
  210. pthread_mutex_unlock(&m_mutex);
  211. #else
  212. LeaveCriticalSection(&m_mutex);
  213. ReleaseSemaphore(m_empty_sem, 1, nullptr);
  214. #endif
  215. return ret;
  216. }
  217. bool try_pop(T &ret)
  218. {
  219. #if defined HAVE_PTHREAD_H
  220. pthread_mutex_lock(&m_mutex);
  221. if (m_count == 0)
  222. {
  223. pthread_mutex_unlock(&m_mutex);
  224. return false;
  225. }
  226. #elif defined _WIN32
  227. DWORD status = WaitForSingleObject(m_full_sem, 0);
  228. if (status == WAIT_TIMEOUT)
  229. return false;
  230. EnterCriticalSection(&m_mutex);
  231. #endif
  232. /* Pop value */
  233. ret = m_values[m_start];
  234. m_start = (m_start + 1) % CAPACITY;
  235. m_count--;
  236. #if defined HAVE_PTHREAD_H
  237. /* If there were pushers waiting, signal the "full" cond var. */
  238. if (m_pushers)
  239. pthread_cond_signal(&m_full_cond);
  240. pthread_mutex_unlock(&m_mutex);
  241. #else
  242. LeaveCriticalSection(&m_mutex);
  243. ReleaseSemaphore(m_empty_sem, 1, nullptr);
  244. #endif
  245. return true;
  246. }
  247. private:
  248. static size_t const CAPACITY = N;
  249. T m_values[CAPACITY];
  250. size_t m_start, m_count;
  251. #if defined HAVE_PTHREAD_H
  252. size_t m_poppers, m_pushers;
  253. pthread_mutex_t m_mutex;
  254. pthread_cond_t m_empty_cond, m_full_cond;
  255. #elif defined _WIN32
  256. HANDLE m_empty_sem, m_full_sem;
  257. CRITICAL_SECTION m_mutex;
  258. #endif
  259. };
  260. class thread_base
  261. {
  262. public:
  263. thread_base(std::function<void(void)> function)
  264. : m_function(function)
  265. {
  266. #if defined HAVE_PTHREAD_H
  267. /* Set the joinable attribute for systems who don't play nice */
  268. pthread_attr_t attr;
  269. pthread_attr_init(&attr);
  270. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  271. pthread_create(&m_thread, &attr, trampoline, this);
  272. #elif defined _WIN32
  273. m_thread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)trampoline,
  274. this, 0, &m_tid);
  275. #endif
  276. }
  277. virtual ~thread_base()
  278. {
  279. #if defined HAVE_PTHREAD_H
  280. pthread_join(m_thread, nullptr);
  281. #elif defined _WIN32
  282. WaitForSingleObject(m_thread, INFINITE);
  283. #endif
  284. }
  285. private:
  286. static void *trampoline(void *data)
  287. {
  288. thread_base *that = (thread_base *)data;
  289. that->m_function();
  290. return nullptr;
  291. }
  292. std::function<void(void)> m_function;
  293. #if defined HAVE_PTHREAD_H
  294. pthread_t m_thread;
  295. #elif defined _WIN32
  296. HANDLE m_thread;
  297. DWORD m_tid;
  298. #endif
  299. };
  300. } /* namespace lol */