threading_impl_posix.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /*************************************************************************
  2. * *
  3. * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
  4. * All rights reserved. Email: [email protected] Web: www.q12.org *
  5. * *
  6. * Threading POSIX implementation file. *
  7. * Copyright (C) 2011-2012 Oleh Derevenko. All rights reserved. *
  8. * e-mail: [email protected] (change all "a" to "e") *
  9. * *
  10. * This library is free software; you can redistribute it and/or *
  11. * modify it under the terms of EITHER: *
  12. * (1) The GNU Lesser General Public License as published by the Free *
  13. * Software Foundation; either version 2.1 of the License, or (at *
  14. * your option) any later version. The text of the GNU Lesser *
  15. * General Public License is included with this library in the *
  16. * file LICENSE.TXT. *
  17. * (2) The BSD-style license that is included with this library in *
  18. * the file LICENSE-BSD.TXT. *
  19. * *
  20. * This library is distributed in the hope that it will be useful, *
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
  23. * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
  24. * *
  25. *************************************************************************/
  26. /*
  27. * Threading POSIX implementation for built-in threading support provider.
  28. */
  29. #ifndef _ODE_THREADING_IMPL_POSIX_H_
  30. #define _ODE_THREADING_IMPL_POSIX_H_
  31. #include <ode/common.h>
  32. #if !defined(_WIN32)
  33. #include "threading_impl_templates.h"
  34. #include "threading_fake_sync.h"
  35. #include "threading_atomics_provs.h"
  36. #if dBUILTIN_THREADING_IMPL_ENABLED
  37. #include <pthread.h>
  38. #include <time.h>
  39. #include <errno.h>
  40. #if !defined(EOK)
  41. #define EOK 0
  42. #endif
  43. #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
  44. #if dBUILTIN_THREADING_IMPL_ENABLED
  45. /************************************************************************/
  46. /* dxCondvarWakeup class implementation */
  47. /************************************************************************/
  48. class dxCondvarWakeup
  49. {
  50. public:
  51. dxCondvarWakeup(): m_waiters_list(NULL), m_signaled_state(false), m_state_is_permanent(false), m_object_initialized(false) {}
  52. ~dxCondvarWakeup() { DoFinalizeObject(); }
  53. bool InitializeObject() { return DoInitializeObject(); }
  54. private:
  55. bool DoInitializeObject();
  56. void DoFinalizeObject();
  57. public:
  58. void ResetWakeup();
  59. void WakeupAThread();
  60. void WakeupAllThreads();
  61. bool WaitWakeup(const dThreadedWaitTime *timeout_time_ptr);
  62. private:
  63. bool BlockAsAWaiter(const dThreadedWaitTime *timeout_time_ptr);
  64. private:
  65. struct dxWaiterInfo
  66. {
  67. dxWaiterInfo(): m_signal_state(false) {}
  68. dxWaiterInfo **m_prev_info_ptr;
  69. dxWaiterInfo *m_next_info;
  70. bool m_signal_state;
  71. };
  72. void RegisterWaiterInList(dxWaiterInfo *waiter_info);
  73. void UnregisterWaiterFromList(dxWaiterInfo *waiter_info);
  74. bool MarkSignaledFirstWaiter();
  75. static bool MarkSignaledFirstWaiterMeaningful(dxWaiterInfo *first_waiter);
  76. bool MarkSignaledAllWaiters();
  77. static bool MarkSignaledAllWaitersMeaningful(dxWaiterInfo *first_waiter);
  78. private:
  79. dxWaiterInfo *m_waiters_list;
  80. bool m_signaled_state;
  81. bool m_state_is_permanent;
  82. bool m_object_initialized;
  83. pthread_mutex_t m_wakeup_mutex;
  84. pthread_cond_t m_wakeup_cond;
  85. };
  86. bool dxCondvarWakeup::DoInitializeObject()
  87. {
  88. dIASSERT(!m_object_initialized);
  89. bool init_result = false;
  90. pthread_condattr_t cond_condattr;
  91. bool mutex_initialized = false, condattr_initialized = false;
  92. do
  93. {
  94. int mutex_result = pthread_mutex_init(&m_wakeup_mutex, NULL);
  95. if (mutex_result != EOK)
  96. {
  97. errno = mutex_result;
  98. break;
  99. }
  100. mutex_initialized = true;
  101. int condattr_init_result = pthread_condattr_init(&cond_condattr);
  102. if (condattr_init_result != EOK)
  103. {
  104. errno = condattr_init_result;
  105. break;
  106. }
  107. condattr_initialized = true;
  108. int condattr_clock_result = pthread_condattr_setclock(&cond_condattr, CLOCK_MONOTONIC);
  109. if (condattr_clock_result != EOK)
  110. {
  111. errno = condattr_clock_result;
  112. break;
  113. }
  114. int cond_result = pthread_cond_init(&m_wakeup_cond, &cond_condattr);
  115. if (cond_result != EOK)
  116. {
  117. errno = cond_result;
  118. break;
  119. }
  120. pthread_condattr_destroy(&cond_condattr); // result can be ignored
  121. m_object_initialized = true;
  122. init_result = true;
  123. }
  124. while (false);
  125. if (!init_result)
  126. {
  127. if (mutex_initialized)
  128. {
  129. if (condattr_initialized)
  130. {
  131. int condattr_destroy_result = pthread_condattr_destroy(&cond_condattr);
  132. dICHECK(condattr_destroy_result == EOK || ((errno = condattr_destroy_result), false));
  133. }
  134. int mutex_destroy_result = pthread_mutex_destroy(&m_wakeup_mutex);
  135. dICHECK(mutex_destroy_result == EOK || ((errno = mutex_destroy_result), false));
  136. }
  137. }
  138. return init_result;
  139. }
  140. void dxCondvarWakeup::DoFinalizeObject()
  141. {
  142. if (m_object_initialized)
  143. {
  144. int cond_result = pthread_cond_destroy(&m_wakeup_cond);
  145. dICHECK(cond_result == EOK || ((errno = cond_result), false));
  146. int mutex_result = pthread_mutex_destroy(&m_wakeup_mutex);
  147. dICHECK(mutex_result == EOK || ((errno = mutex_result), false));
  148. m_object_initialized = false;
  149. }
  150. }
  151. void dxCondvarWakeup::ResetWakeup()
  152. {
  153. int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
  154. dICHECK(lock_result == EOK || ((errno = lock_result), false));
  155. m_signaled_state = false;
  156. m_state_is_permanent = false;
  157. int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
  158. dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
  159. }
  160. void dxCondvarWakeup::WakeupAThread()
  161. {
  162. int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
  163. dICHECK(lock_result == EOK || ((errno = lock_result), false));
  164. dIASSERT(!m_state_is_permanent); // Wakeup should not be used after permanent signal
  165. if (!m_signaled_state)
  166. {
  167. if (MarkSignaledFirstWaiter())
  168. {
  169. // All threads must be woken up regardless to the fact that only one waiter is marked.
  170. // It is not possible to wake up a chosen thread personally
  171. // and if a random thread is woken up it can't know if there was a condition signal for it
  172. // or the sleep was interrupted by POSIX signal.
  173. // On the other hand, without this it is not possible to guarantee that a thread
  174. // will be woken up per each WakeupAThread() call if there is more than one waiter
  175. // and wakeup requests will not accumulate if there are no waiters.
  176. int broadcast_result = pthread_cond_broadcast(&m_wakeup_cond);
  177. dICHECK(broadcast_result == EOK || ((errno = broadcast_result), false));
  178. }
  179. else
  180. {
  181. m_signaled_state = true;
  182. }
  183. }
  184. int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
  185. dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
  186. }
  187. void dxCondvarWakeup::WakeupAllThreads()
  188. {
  189. int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
  190. dICHECK(lock_result == EOK || ((errno = lock_result), false));
  191. m_state_is_permanent = true;
  192. if (!m_signaled_state)
  193. {
  194. m_signaled_state = true;
  195. if (MarkSignaledAllWaiters())
  196. {
  197. int broadcast_result = pthread_cond_broadcast(&m_wakeup_cond);
  198. dICHECK(broadcast_result == EOK || ((errno = broadcast_result), false));
  199. }
  200. }
  201. int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
  202. dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
  203. }
  204. bool dxCondvarWakeup::WaitWakeup(const dThreadedWaitTime *timeout_time_ptr)
  205. {
  206. bool wait_result;
  207. int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
  208. dICHECK(lock_result == EOK || ((errno = lock_result), false));
  209. if (!m_signaled_state)
  210. {
  211. if (!timeout_time_ptr || timeout_time_ptr->wait_nsec != 0 || timeout_time_ptr->wait_sec != 0)
  212. {
  213. wait_result = BlockAsAWaiter(timeout_time_ptr);
  214. }
  215. else
  216. {
  217. wait_result = false;
  218. }
  219. }
  220. else
  221. {
  222. m_signaled_state = m_state_is_permanent;
  223. wait_result = true;
  224. }
  225. int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
  226. dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
  227. return wait_result;
  228. }
  229. bool dxCondvarWakeup::BlockAsAWaiter(const dThreadedWaitTime *timeout_time_ptr)
  230. {
  231. bool wait_result = false;
  232. dxWaiterInfo waiter_info;
  233. RegisterWaiterInList(&waiter_info);
  234. timespec wakeup_time;
  235. if (timeout_time_ptr != NULL)
  236. {
  237. timespec current_time;
  238. int clock_result = clock_gettime(CLOCK_MONOTONIC, &current_time);
  239. dICHECK(clock_result != -1);
  240. time_t wakeup_sec = current_time.tv_sec + timeout_time_ptr->wait_sec;
  241. unsigned long wakeup_nsec = current_time.tv_nsec + timeout_time_ptr->wait_nsec;
  242. if (wakeup_nsec >= 1000000000)
  243. {
  244. wakeup_nsec -= 1000000000;
  245. wakeup_sec += 1;
  246. }
  247. wakeup_time.tv_sec = wakeup_sec;
  248. wakeup_time.tv_nsec = wakeup_nsec;
  249. }
  250. while (true)
  251. {
  252. int cond_result = (timeout_time_ptr != NULL)
  253. ? pthread_cond_timedwait(&m_wakeup_cond, &m_wakeup_mutex, &wakeup_time)
  254. : pthread_cond_wait(&m_wakeup_cond, &m_wakeup_mutex);
  255. dICHECK(cond_result == EOK || cond_result == ETIMEDOUT || ((errno = cond_result), false));
  256. if (waiter_info.m_signal_state)
  257. {
  258. wait_result = true;
  259. break;
  260. }
  261. if (cond_result == ETIMEDOUT)
  262. {
  263. dIASSERT(timeout_time_ptr != NULL);
  264. break;
  265. }
  266. }
  267. UnregisterWaiterFromList(&waiter_info);
  268. return wait_result;
  269. }
  270. void dxCondvarWakeup::RegisterWaiterInList(dxWaiterInfo *waiter_info)
  271. {
  272. dxWaiterInfo *const first_waiter = m_waiters_list;
  273. if (first_waiter == NULL)
  274. {
  275. waiter_info->m_next_info = waiter_info;
  276. waiter_info->m_prev_info_ptr = &waiter_info->m_next_info;
  277. m_waiters_list = waiter_info;
  278. }
  279. else
  280. {
  281. waiter_info->m_next_info = first_waiter;
  282. waiter_info->m_prev_info_ptr = first_waiter->m_prev_info_ptr;
  283. *first_waiter->m_prev_info_ptr = waiter_info;
  284. first_waiter->m_prev_info_ptr = &waiter_info->m_next_info;
  285. }
  286. }
  287. void dxCondvarWakeup::UnregisterWaiterFromList(dxWaiterInfo *waiter_info)
  288. {
  289. dxWaiterInfo *next_info = waiter_info->m_next_info;
  290. if (next_info == waiter_info)
  291. {
  292. m_waiters_list = NULL;
  293. }
  294. else
  295. {
  296. next_info->m_prev_info_ptr = waiter_info->m_prev_info_ptr;
  297. *waiter_info->m_prev_info_ptr = next_info;
  298. if (waiter_info == m_waiters_list)
  299. {
  300. m_waiters_list = next_info;
  301. }
  302. }
  303. }
  304. bool dxCondvarWakeup::MarkSignaledFirstWaiter()
  305. {
  306. bool waiter_found = false;
  307. dxWaiterInfo *const first_waiter = m_waiters_list;
  308. if (first_waiter)
  309. {
  310. waiter_found = MarkSignaledFirstWaiterMeaningful(first_waiter);
  311. }
  312. return waiter_found;
  313. }
  314. bool dxCondvarWakeup::MarkSignaledFirstWaiterMeaningful(dxWaiterInfo *first_waiter)
  315. {
  316. bool waiter_found = false;
  317. dxWaiterInfo *current_waiter = first_waiter;
  318. while (true)
  319. {
  320. if (!current_waiter->m_signal_state)
  321. {
  322. current_waiter->m_signal_state = true;
  323. waiter_found = true;
  324. break;
  325. }
  326. current_waiter = current_waiter->m_next_info;
  327. if (current_waiter == first_waiter)
  328. {
  329. break;
  330. }
  331. }
  332. return waiter_found;
  333. }
  334. bool dxCondvarWakeup::MarkSignaledAllWaiters()
  335. {
  336. bool waiter_found = false;
  337. dxWaiterInfo *const first_waiter = m_waiters_list;
  338. if (first_waiter)
  339. {
  340. waiter_found = MarkSignaledAllWaitersMeaningful(first_waiter);
  341. }
  342. return waiter_found;
  343. }
  344. bool dxCondvarWakeup::MarkSignaledAllWaitersMeaningful(dxWaiterInfo *first_waiter)
  345. {
  346. bool waiter_found = false;
  347. dxWaiterInfo *current_waiter = first_waiter;
  348. while (true)
  349. {
  350. if (!current_waiter->m_signal_state)
  351. {
  352. current_waiter->m_signal_state = true;
  353. waiter_found = true;
  354. }
  355. current_waiter = current_waiter->m_next_info;
  356. if (current_waiter == first_waiter)
  357. {
  358. break;
  359. }
  360. }
  361. return waiter_found;
  362. }
  363. /************************************************************************/
  364. /* dxMutexMutex class implementation */
  365. /************************************************************************/
  366. class dxMutexMutex
  367. {
  368. public:
  369. dxMutexMutex(): m_mutex_allocated(false) {}
  370. ~dxMutexMutex() { DoFinalizeObject(); }
  371. bool InitializeObject() { return DoInitializeObject(); }
  372. private:
  373. bool DoInitializeObject();
  374. void DoFinalizeObject();
  375. public:
  376. void LockMutex();
  377. bool TryLockMutex();
  378. void UnlockMutex();
  379. private:
  380. pthread_mutex_t m_mutex_instance;
  381. bool m_mutex_allocated;
  382. };
  383. bool dxMutexMutex::DoInitializeObject()
  384. {
  385. dIASSERT(!m_mutex_allocated);
  386. bool init_result = false;
  387. do
  388. {
  389. int mutex_result = pthread_mutex_init(&m_mutex_instance, NULL);
  390. if (mutex_result != EOK)
  391. {
  392. errno = mutex_result;
  393. break;
  394. }
  395. m_mutex_allocated = true;
  396. init_result = true;
  397. }
  398. while (false);
  399. return init_result;
  400. }
  401. void dxMutexMutex::DoFinalizeObject()
  402. {
  403. if (m_mutex_allocated)
  404. {
  405. int mutex_result = pthread_mutex_destroy(&m_mutex_instance);
  406. dICHECK(mutex_result == EOK || ((errno = mutex_result), false));
  407. m_mutex_allocated = false;
  408. }
  409. }
  410. void dxMutexMutex::LockMutex()
  411. {
  412. int lock_result = pthread_mutex_lock(&m_mutex_instance);
  413. dICHECK(lock_result == EOK || ((errno = lock_result), false));
  414. }
  415. bool dxMutexMutex::TryLockMutex()
  416. {
  417. int trylock_result = pthread_mutex_trylock(&m_mutex_instance);
  418. dICHECK(trylock_result == EOK || trylock_result == EBUSY || ((errno = trylock_result), false));
  419. return trylock_result == EOK;
  420. }
  421. void dxMutexMutex::UnlockMutex()
  422. {
  423. int unlock_result = pthread_mutex_unlock(&m_mutex_instance);
  424. dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
  425. }
  426. #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
  427. /************************************************************************/
  428. /* Self-threaded job list definition */
  429. /************************************************************************/
  430. typedef dxtemplateJobListContainer<dxFakeLull, dxFakeMutex, dxFakeAtomicsProvider> dxSelfThreadedJobListContainer;
  431. typedef dxtemplateJobListSelfHandler<dxSelfWakeup, dxSelfThreadedJobListContainer> dxSelfThreadedJobListHandler;
  432. typedef dxtemplateThreadingImplementation<dxSelfThreadedJobListContainer, dxSelfThreadedJobListHandler> dxSelfThreadedThreading;
  433. #if dBUILTIN_THREADING_IMPL_ENABLED
  434. /************************************************************************/
  435. /* Multi-threaded job list definition */
  436. /************************************************************************/
  437. typedef dxtemplateJobListContainer<dxtemplateThreadedLull<dxCondvarWakeup, dxOUAtomicsProvider, false>, dxMutexMutex, dxOUAtomicsProvider> dxMultiThreadedJobListContainer;
  438. typedef dxtemplateJobListThreadedHandler<dxCondvarWakeup, dxMultiThreadedJobListContainer> dxMultiThreadedJobListHandler;
  439. typedef dxtemplateThreadingImplementation<dxMultiThreadedJobListContainer, dxMultiThreadedJobListHandler> dxMultiThreadedThreading;
  440. #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
  441. #endif // #if !defined(_WIN32)
  442. #endif // #ifndef _ODE_THREADING_IMPL_POSIX_H_