Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

295 wiersze
7.1 KiB

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