Open Dynamics Engine
threading_impl_posix.h
1 /*************************************************************************
2  * *
3  * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
4  * All rights reserved. Email: russ@q12.org Web: www.q12.org *
5  * *
6  * Threading POSIX implementation file. *
7  * Copyright (C) 2011-2012 Oleh Derevenko. All rights reserved. *
8  * e-mail: odar@eleks.com (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 /*
28  * Threading POSIX implementation for built-in threading support provider.
29  */
30 
31 
32 #ifndef _ODE_THREADING_IMPL_POSIX_H_
33 #define _ODE_THREADING_IMPL_POSIX_H_
34 
35 
36 #include <ode/common.h>
37 
38 
39 #if !defined(_WIN32)
40 
41 
42 #include "threading_impl_templates.h"
43 #include "threading_fake_sync.h"
44 #include "threading_atomics_provs.h"
45 
46 #if dBUILTIN_THREADING_IMPL_ENABLED
47 
48 #include <pthread.h>
49 #include <time.h>
50 #include <errno.h>
51 
52 #if !defined(EOK)
53 #define EOK 0
54 #endif
55 
56 
57 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
58 
59 
60 #if dBUILTIN_THREADING_IMPL_ENABLED
61 
62 /************************************************************************/
63 /* dxCondvarWakeup class implementation */
64 /************************************************************************/
65 
66 class dxCondvarWakeup
67 {
68 public:
69  dxCondvarWakeup(): m_waiters_list(NULL), m_signaled_state(false), m_state_is_permanent(false), m_object_initialized(false) {}
70  ~dxCondvarWakeup() { DoFinalizeObject(); }
71 
72  bool InitializeObject() { return DoInitializeObject(); }
73 
74 private:
75  bool DoInitializeObject();
76  void DoFinalizeObject();
77 
78 public:
79  void ResetWakeup();
80  void WakeupAThread();
81  void WakeupAllThreads();
82 
83  bool WaitWakeup(const dThreadedWaitTime *timeout_time_ptr);
84 
85 private:
86  bool BlockAsAWaiter(const dThreadedWaitTime *timeout_time_ptr);
87 
88 private:
89  struct dxWaiterInfo
90  {
91  dxWaiterInfo(): m_signal_state(false) {}
92 
93  dxWaiterInfo **m_prev_info_ptr;
94  dxWaiterInfo *m_next_info;
95  bool m_signal_state;
96  };
97 
98  void RegisterWaiterInList(dxWaiterInfo *waiter_info);
99  void UnregisterWaiterFromList(dxWaiterInfo *waiter_info);
100 
101  bool MarkSignaledFirstWaiter();
102  static bool MarkSignaledFirstWaiterMeaningful(dxWaiterInfo *first_waiter);
103  bool MarkSignaledAllWaiters();
104  static bool MarkSignaledAllWaitersMeaningful(dxWaiterInfo *first_waiter);
105 
106 private:
107  dxWaiterInfo *m_waiters_list;
108  bool m_signaled_state;
109  bool m_state_is_permanent;
110  bool m_object_initialized;
111  pthread_mutex_t m_wakeup_mutex;
112  pthread_cond_t m_wakeup_cond;
113 };
114 
115 
116 bool dxCondvarWakeup::DoInitializeObject()
117 {
118  dIASSERT(!m_object_initialized);
119 
120  bool init_result = false;
121 
122  pthread_condattr_t cond_condattr;
123  bool mutex_initialized = false, condattr_initialized = false;
124 
125  do
126  {
127  int mutex_result = pthread_mutex_init(&m_wakeup_mutex, NULL);
128  if (mutex_result != EOK)
129  {
130  errno = mutex_result;
131  break;
132  }
133 
134  mutex_initialized = true;
135 
136  int condattr_init_result = pthread_condattr_init(&cond_condattr);
137  if (condattr_init_result != EOK)
138  {
139  errno = condattr_init_result;
140  break;
141  }
142 
143  condattr_initialized = true;
144 
145  int condattr_clock_result = pthread_condattr_setclock(&cond_condattr, CLOCK_MONOTONIC);
146  if (condattr_clock_result != EOK)
147  {
148  errno = condattr_clock_result;
149  break;
150  }
151 
152  int cond_result = pthread_cond_init(&m_wakeup_cond, &cond_condattr);
153  if (cond_result != EOK)
154  {
155  errno = cond_result;
156  break;
157  }
158 
159  pthread_condattr_destroy(&cond_condattr); // result can be ignored
160 
161  m_object_initialized = true;
162  init_result = true;
163  }
164  while (false);
165 
166  if (!init_result)
167  {
168  if (mutex_initialized)
169  {
170  if (condattr_initialized)
171  {
172  int condattr_destroy_result = pthread_condattr_destroy(&cond_condattr);
173  dICHECK(condattr_destroy_result == EOK || ((errno = condattr_destroy_result), false));
174  }
175 
176  int mutex_destroy_result = pthread_mutex_destroy(&m_wakeup_mutex);
177  dICHECK(mutex_destroy_result == EOK || ((errno = mutex_destroy_result), false));
178  }
179  }
180 
181  return init_result;
182 
183 }
184 
185 void dxCondvarWakeup::DoFinalizeObject()
186 {
187  if (m_object_initialized)
188  {
189  int cond_result = pthread_cond_destroy(&m_wakeup_cond);
190  dICHECK(cond_result == EOK || ((errno = cond_result), false));
191 
192  int mutex_result = pthread_mutex_destroy(&m_wakeup_mutex);
193  dICHECK(mutex_result == EOK || ((errno = mutex_result), false));
194 
195  m_object_initialized = false;
196  }
197 }
198 
199 
200 void dxCondvarWakeup::ResetWakeup()
201 {
202  int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
203  dICHECK(lock_result == EOK || ((errno = lock_result), false));
204 
205  m_signaled_state = false;
206  m_state_is_permanent = false;
207 
208  int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
209  dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
210 }
211 
212 void dxCondvarWakeup::WakeupAThread()
213 {
214  int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
215  dICHECK(lock_result == EOK || ((errno = lock_result), false));
216 
217  dIASSERT(!m_state_is_permanent); // Wakeup should not be used after permanent signal
218 
219  if (!m_signaled_state)
220  {
221  if (MarkSignaledFirstWaiter())
222  {
223  // All threads must be woken up regardless to the fact that only one waiter is marked.
224  // It is not possible to wake up a chosen thread personally
225  // and if a random thread is woken up it can't know if there was a condition signal for it
226  // or the sleep was interrupted by POSIX signal.
227  // On the other hand, without this it is not possible to guarantee that a thread
228  // will be woken up per each WakeupAThread() call if there is more than one waiter
229  // and wakeup requests will not accumulate if there are no waiters.
230  int broadcast_result = pthread_cond_broadcast(&m_wakeup_cond);
231  dICHECK(broadcast_result == EOK || ((errno = broadcast_result), false));
232  }
233  else
234  {
235  m_signaled_state = true;
236  }
237  }
238 
239  int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
240  dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
241 }
242 
243 void dxCondvarWakeup::WakeupAllThreads()
244 {
245  int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
246  dICHECK(lock_result == EOK || ((errno = lock_result), false));
247 
248  m_state_is_permanent = true;
249 
250  if (!m_signaled_state)
251  {
252  m_signaled_state = true;
253 
254  if (MarkSignaledAllWaiters())
255  {
256  int broadcast_result = pthread_cond_broadcast(&m_wakeup_cond);
257  dICHECK(broadcast_result == EOK || ((errno = broadcast_result), false));
258  }
259  }
260 
261  int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
262  dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
263 }
264 
265 
266 bool dxCondvarWakeup::WaitWakeup(const dThreadedWaitTime *timeout_time_ptr)
267 {
268  bool wait_result;
269 
270  int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
271  dICHECK(lock_result == EOK || ((errno = lock_result), false));
272 
273  if (!m_signaled_state)
274  {
275  if (!timeout_time_ptr || timeout_time_ptr->wait_nsec != 0 || timeout_time_ptr->wait_sec != 0)
276  {
277  wait_result = BlockAsAWaiter(timeout_time_ptr);
278  }
279  else
280  {
281  wait_result = false;
282  }
283  }
284  else
285  {
286  m_signaled_state = m_state_is_permanent;
287  wait_result = true;
288  }
289 
290  int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
291  dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
292 
293  return wait_result;
294 }
295 
296 bool dxCondvarWakeup::BlockAsAWaiter(const dThreadedWaitTime *timeout_time_ptr)
297 {
298  bool wait_result = false;
299 
300  dxWaiterInfo waiter_info;
301  RegisterWaiterInList(&waiter_info);
302 
303  timespec wakeup_time;
304 
305  if (timeout_time_ptr != NULL)
306  {
307  timespec current_time;
308 
309  int clock_result = clock_gettime(CLOCK_MONOTONIC, &current_time);
310  dICHECK(clock_result != -1);
311 
312  time_t wakeup_sec = current_time.tv_sec + timeout_time_ptr->wait_sec;
313  unsigned long wakeup_nsec = current_time.tv_nsec + timeout_time_ptr->wait_nsec;
314 
315  if (wakeup_nsec >= 1000000000)
316  {
317  wakeup_nsec -= 1000000000;
318  wakeup_sec += 1;
319  }
320 
321  wakeup_time.tv_sec = wakeup_sec;
322  wakeup_time.tv_nsec = wakeup_nsec;
323  }
324 
325  while (true)
326  {
327  int cond_result = (timeout_time_ptr != NULL)
328  ? pthread_cond_timedwait(&m_wakeup_cond, &m_wakeup_mutex, &wakeup_time)
329  : pthread_cond_wait(&m_wakeup_cond, &m_wakeup_mutex);
330  dICHECK(cond_result == EOK || cond_result == ETIMEDOUT || ((errno = cond_result), false));
331 
332  if (waiter_info.m_signal_state)
333  {
334  wait_result = true;
335  break;
336  }
337 
338  if (cond_result == ETIMEDOUT)
339  {
340  dIASSERT(timeout_time_ptr != NULL);
341  break;
342  }
343  }
344 
345  UnregisterWaiterFromList(&waiter_info);
346 
347  return wait_result;
348 }
349 
350 
351 void dxCondvarWakeup::RegisterWaiterInList(dxWaiterInfo *waiter_info)
352 {
353  dxWaiterInfo *const first_waiter = m_waiters_list;
354 
355  if (first_waiter == NULL)
356  {
357  waiter_info->m_next_info = waiter_info;
358  waiter_info->m_prev_info_ptr = &waiter_info->m_next_info;
359  m_waiters_list = waiter_info;
360  }
361  else
362  {
363  waiter_info->m_next_info = first_waiter;
364  waiter_info->m_prev_info_ptr = first_waiter->m_prev_info_ptr;
365  *first_waiter->m_prev_info_ptr = waiter_info;
366  first_waiter->m_prev_info_ptr = &waiter_info->m_next_info;
367  }
368 }
369 
370 void dxCondvarWakeup::UnregisterWaiterFromList(dxWaiterInfo *waiter_info)
371 {
372  dxWaiterInfo *next_info = waiter_info->m_next_info;
373 
374  if (next_info == waiter_info)
375  {
376  m_waiters_list = NULL;
377  }
378  else
379  {
380  next_info->m_prev_info_ptr = waiter_info->m_prev_info_ptr;
381  *waiter_info->m_prev_info_ptr = next_info;
382 
383  if (waiter_info == m_waiters_list)
384  {
385  m_waiters_list = next_info;
386  }
387  }
388 }
389 
390 
391 bool dxCondvarWakeup::MarkSignaledFirstWaiter()
392 {
393  bool waiter_found = false;
394 
395  dxWaiterInfo *const first_waiter = m_waiters_list;
396 
397  if (first_waiter)
398  {
399  waiter_found = MarkSignaledFirstWaiterMeaningful(first_waiter);
400  }
401 
402  return waiter_found;
403 }
404 
405 bool dxCondvarWakeup::MarkSignaledFirstWaiterMeaningful(dxWaiterInfo *first_waiter)
406 {
407  bool waiter_found = false;
408 
409  dxWaiterInfo *current_waiter = first_waiter;
410 
411  while (true)
412  {
413  if (!current_waiter->m_signal_state)
414  {
415  current_waiter->m_signal_state = true;
416  waiter_found = true;
417  break;
418  }
419 
420  current_waiter = current_waiter->m_next_info;
421  if (current_waiter == first_waiter)
422  {
423  break;
424  }
425  }
426 
427  return waiter_found;
428 }
429 
430 bool dxCondvarWakeup::MarkSignaledAllWaiters()
431 {
432  bool waiter_found = false;
433 
434  dxWaiterInfo *const first_waiter = m_waiters_list;
435 
436  if (first_waiter)
437  {
438  waiter_found = MarkSignaledAllWaitersMeaningful(first_waiter);
439  }
440 
441  return waiter_found;
442 }
443 
444 bool dxCondvarWakeup::MarkSignaledAllWaitersMeaningful(dxWaiterInfo *first_waiter)
445 {
446  bool waiter_found = false;
447 
448  dxWaiterInfo *current_waiter = first_waiter;
449 
450  while (true)
451  {
452  if (!current_waiter->m_signal_state)
453  {
454  current_waiter->m_signal_state = true;
455  waiter_found = true;
456  }
457 
458  current_waiter = current_waiter->m_next_info;
459  if (current_waiter == first_waiter)
460  {
461  break;
462  }
463  }
464 
465  return waiter_found;
466 }
467 
468 
469 /************************************************************************/
470 /* dxMutexMutex class implementation */
471 /************************************************************************/
472 
473 class dxMutexMutex
474 {
475 public:
476  dxMutexMutex(): m_mutex_allocated(false) {}
477  ~dxMutexMutex() { DoFinalizeObject(); }
478 
479  bool InitializeObject() { return DoInitializeObject(); }
480 
481 private:
482  bool DoInitializeObject();
483  void DoFinalizeObject();
484 
485 public:
486  void LockMutex();
487  bool TryLockMutex();
488  void UnlockMutex();
489 
490 private:
491  pthread_mutex_t m_mutex_instance;
492  bool m_mutex_allocated;
493 };
494 
495 
496 bool dxMutexMutex::DoInitializeObject()
497 {
498  dIASSERT(!m_mutex_allocated);
499 
500  bool init_result = false;
501 
502  do
503  {
504  int mutex_result = pthread_mutex_init(&m_mutex_instance, NULL);
505  if (mutex_result != EOK)
506  {
507  errno = mutex_result;
508  break;
509  }
510 
511  m_mutex_allocated = true;
512  init_result = true;
513  }
514  while (false);
515 
516  return init_result;
517 }
518 
519 void dxMutexMutex::DoFinalizeObject()
520 {
521  if (m_mutex_allocated)
522  {
523  int mutex_result = pthread_mutex_destroy(&m_mutex_instance);
524  dICHECK(mutex_result == EOK || ((errno = mutex_result), false));
525 
526  m_mutex_allocated = false;
527  }
528 }
529 
530 
531 void dxMutexMutex::LockMutex()
532 {
533  int lock_result = pthread_mutex_lock(&m_mutex_instance);
534  dICHECK(lock_result == EOK || ((errno = lock_result), false));
535 }
536 
537 bool dxMutexMutex::TryLockMutex()
538 {
539  int trylock_result = pthread_mutex_trylock(&m_mutex_instance);
540  dICHECK(trylock_result == EOK || trylock_result == EBUSY || ((errno = trylock_result), false));
541 
542  return trylock_result == EOK;
543 }
544 
545 void dxMutexMutex::UnlockMutex()
546 {
547  int unlock_result = pthread_mutex_unlock(&m_mutex_instance);
548  dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
549 }
550 
551 
552 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
553 
554 
555 /************************************************************************/
556 /* Self-threaded job list definition */
557 /************************************************************************/
558 
562 
563 
564 #if dBUILTIN_THREADING_IMPL_ENABLED
565 
566 /************************************************************************/
567 /* Multi-threaded job list definition */
568 /************************************************************************/
569 
570 typedef dxtemplateJobListContainer<dxtemplateThreadedLull<dxCondvarWakeup, dxOUAtomicsProvider, false>, dxMutexMutex, dxOUAtomicsProvider> dxMultiThreadedJobListContainer;
571 typedef dxtemplateJobListThreadedHandler<dxCondvarWakeup, dxMultiThreadedJobListContainer> dxMultiThreadedJobListHandler;
573 
574 
575 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
576 
577 
578 #endif // #if !defined(_WIN32)
579 
580 
581 #endif // #ifndef _ODE_THREADING_IMPL_POSIX_H_
Definition: threading_impl_templates.h:209
Definition: threading_impl_templates.h:444
Definition: threading_impl_templates.h:366
Definition: threading.h:154