diff options
Diffstat (limited to 'libs/ode-0.16.1/ode/src/threading_impl_posix.h')
-rw-r--r-- | libs/ode-0.16.1/ode/src/threading_impl_posix.h | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/libs/ode-0.16.1/ode/src/threading_impl_posix.h b/libs/ode-0.16.1/ode/src/threading_impl_posix.h new file mode 100644 index 0000000..0aaf4ae --- /dev/null +++ b/libs/ode-0.16.1/ode/src/threading_impl_posix.h @@ -0,0 +1,638 @@ +/*************************************************************************
+ * *
+ * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
+ * All rights reserved. Email: russ@q12.org Web: www.q12.org *
+ * *
+ * Threading POSIX implementation file. *
+ * Copyright (C) 2011-2019 Oleh Derevenko. All rights reserved. *
+ * e-mail: odar@eleks.com (change all "a" to "e") *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of EITHER: *
+ * (1) The GNU Lesser General Public License as published by the Free *
+ * Software Foundation; either version 2.1 of the License, or (at *
+ * your option) any later version. The text of the GNU Lesser *
+ * General Public License is included with this library in the *
+ * file LICENSE.TXT. *
+ * (2) The BSD-style license that is included with this library in *
+ * the file LICENSE-BSD.TXT. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
+ * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
+ * *
+ *************************************************************************/
+
+/*
+ * Threading POSIX implementation for built-in threading support provider.
+ */
+
+
+#ifndef _ODE_THREADING_IMPL_POSIX_H_
+#define _ODE_THREADING_IMPL_POSIX_H_
+
+
+#include <ode/common.h>
+
+
+#if !defined(_WIN32)
+
+
+#include "threading_impl_templates.h"
+#include "threading_fake_sync.h"
+#include "threading_atomics_provs.h"
+
+
+#if dBUILTIN_THREADING_IMPL_ENABLED
+
+#include <pthread.h>
+#include <time.h>
+#include <errno.h>
+
+#if !defined(EOK)
+#define EOK 0
+#endif
+
+
+#if defined(__APPLE__)
+
+#if HAVE_GETTIMEOFDAY
+
+#include <sys/time.h>
+
+#if !defined(CLOCK_MONOTONIC)
+#define CLOCK_MONOTONIC 2
+#endif
+
+static inline
+int _condvar_clock_gettime(int clock_type, timespec *ts)
+{
+ (void)clock_type; // Unused
+ timeval tv;
+ return gettimeofday(&tv, NULL) == 0 ? (ts->tv_sec = tv.tv_sec, ts->tv_nsec = tv.tv_usec * 1000, 0) : (-1);
+}
+
+
+#else // #if !HAVE_GETTIMEOFDAY
+
+#error It is necessary to check manuals for the correct way of getting condvar wait time for this Apple system
+
+
+#endif // #if !HAVE_GETTIMEOFDAY
+
+
+#else // #if !defined(__APPLE__)
+
+#if !HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_NO_PTHREAD_CONDATTR_SETCLOCK
+
+// The code must be compiled without autoconf run, having the project generated by other means.
+// Assume the pthread_condattr_setclock() is available as it is true in most cases and it is the best we can do in such cases.
+#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1
+
+
+#endif // #if !HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_NO_PTHREAD_CONDATTR_SETCLOCK
+
+
+#if HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+static inline
+int _condvar_clock_gettime(int clock_type, timespec *ts)
+{
+ return clock_gettime(clock_type, ts);
+}
+
+
+#else // #if !HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+#error It is necessary to check manuals for the correct way of getting condvar wait time for this system
+
+
+#endif // #if !HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+
+#endif // #if !defined(__APPLE__)
+
+
+/************************************************************************/
+/* dxCondvarWakeup class implementation */
+/************************************************************************/
+
+class dxCondvarWakeup
+{
+public:
+ dxCondvarWakeup(): m_waiters_list(NULL), m_signaled_state(false), m_state_is_permanent(false), m_object_initialized(false) {}
+ ~dxCondvarWakeup() { DoFinalizeObject(); }
+
+ bool InitializeObject() { return DoInitializeObject(); }
+
+private:
+ bool DoInitializeObject();
+ void DoFinalizeObject();
+
+public:
+ void ResetWakeup();
+ void WakeupAThread();
+ void WakeupAllThreads();
+
+ bool WaitWakeup(const dThreadedWaitTime *timeout_time_ptr);
+
+private:
+ bool BlockAsAWaiter(const dThreadedWaitTime *timeout_time_ptr);
+
+private:
+ struct dxWaiterInfo
+ {
+ dxWaiterInfo(): m_signal_state(false) {}
+
+ dxWaiterInfo **m_prev_info_ptr;
+ dxWaiterInfo *m_next_info;
+ bool m_signal_state;
+ };
+
+ void RegisterWaiterInList(dxWaiterInfo *waiter_info);
+ void UnregisterWaiterFromList(dxWaiterInfo *waiter_info);
+
+ bool MarkSignaledFirstWaiter();
+ static bool MarkSignaledFirstWaiterMeaningful(dxWaiterInfo *first_waiter);
+ bool MarkSignaledAllWaiters();
+ static bool MarkSignaledAllWaitersMeaningful(dxWaiterInfo *first_waiter);
+
+private:
+ dxWaiterInfo *m_waiters_list;
+ bool m_signaled_state;
+ bool m_state_is_permanent;
+ bool m_object_initialized;
+ pthread_mutex_t m_wakeup_mutex;
+ pthread_cond_t m_wakeup_cond;
+};
+
+
+bool dxCondvarWakeup::DoInitializeObject()
+{
+ dIASSERT(!m_object_initialized);
+
+ bool init_result = false;
+
+ pthread_condattr_t cond_condattr;
+ bool mutex_initialized = false, condattr_initialized = false;
+
+ do
+ {
+ int mutex_result = pthread_mutex_init(&m_wakeup_mutex, NULL);
+ if (mutex_result != EOK)
+ {
+ errno = mutex_result;
+ break;
+ }
+
+ mutex_initialized = true;
+
+ int condattr_init_result = pthread_condattr_init(&cond_condattr);
+ if (condattr_init_result != EOK)
+ {
+ errno = condattr_init_result;
+ break;
+ }
+
+ condattr_initialized = true;
+
+#if HAVE_PTHREAD_CONDATTR_SETCLOCK
+ int condattr_clock_result = pthread_condattr_setclock(&cond_condattr, CLOCK_MONOTONIC);
+ if (condattr_clock_result != EOK)
+ {
+ errno = condattr_clock_result;
+ break;
+ }
+#endif // #if HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+ int cond_result = pthread_cond_init(&m_wakeup_cond, &cond_condattr);
+ if (cond_result != EOK)
+ {
+ errno = cond_result;
+ break;
+ }
+
+ pthread_condattr_destroy(&cond_condattr); // result can be ignored
+
+ m_object_initialized = true;
+ init_result = true;
+ }
+ while (false);
+
+ if (!init_result)
+ {
+ if (mutex_initialized)
+ {
+ if (condattr_initialized)
+ {
+ int condattr_destroy_result = pthread_condattr_destroy(&cond_condattr);
+ dICHECK(condattr_destroy_result == EOK || ((errno = condattr_destroy_result), false));
+ }
+
+ int mutex_destroy_result = pthread_mutex_destroy(&m_wakeup_mutex);
+ dICHECK(mutex_destroy_result == EOK || ((errno = mutex_destroy_result), false));
+ }
+ }
+
+ return init_result;
+
+}
+
+void dxCondvarWakeup::DoFinalizeObject()
+{
+ if (m_object_initialized)
+ {
+ int cond_result = pthread_cond_destroy(&m_wakeup_cond);
+ dICHECK(cond_result == EOK || ((errno = cond_result), false));
+
+ int mutex_result = pthread_mutex_destroy(&m_wakeup_mutex);
+ dICHECK(mutex_result == EOK || ((errno = mutex_result), false));
+
+ m_object_initialized = false;
+ }
+}
+
+
+void dxCondvarWakeup::ResetWakeup()
+{
+ int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
+ dICHECK(lock_result == EOK || ((errno = lock_result), false));
+
+ m_signaled_state = false;
+ m_state_is_permanent = false;
+
+ int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
+ dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
+}
+
+void dxCondvarWakeup::WakeupAThread()
+{
+ int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
+ dICHECK(lock_result == EOK || ((errno = lock_result), false));
+
+ dIASSERT(!m_state_is_permanent); // Wakeup should not be used after permanent signal
+
+ if (!m_signaled_state)
+ {
+ if (MarkSignaledFirstWaiter())
+ {
+ // All threads must be woken up regardless to the fact that only one waiter is marked.
+ // It is not possible to wake up a chosen thread personally
+ // and if a random thread is woken up it can't know if there was a condition signal for it
+ // or the sleep was interrupted by POSIX signal.
+ // On the other hand, without this it is not possible to guarantee that a thread
+ // will be woken up per each WakeupAThread() call if there is more than one waiter
+ // and wakeup requests will not accumulate if there are no waiters.
+ int broadcast_result = pthread_cond_broadcast(&m_wakeup_cond);
+ dICHECK(broadcast_result == EOK || ((errno = broadcast_result), false));
+ }
+ else
+ {
+ m_signaled_state = true;
+ }
+ }
+
+ int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
+ dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
+}
+
+void dxCondvarWakeup::WakeupAllThreads()
+{
+ int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
+ dICHECK(lock_result == EOK || ((errno = lock_result), false));
+
+ m_state_is_permanent = true;
+
+ if (!m_signaled_state)
+ {
+ m_signaled_state = true;
+
+ if (MarkSignaledAllWaiters())
+ {
+ int broadcast_result = pthread_cond_broadcast(&m_wakeup_cond);
+ dICHECK(broadcast_result == EOK || ((errno = broadcast_result), false));
+ }
+ }
+
+ int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
+ dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
+}
+
+
+bool dxCondvarWakeup::WaitWakeup(const dThreadedWaitTime *timeout_time_ptr)
+{
+ bool wait_result;
+
+ int lock_result = pthread_mutex_lock(&m_wakeup_mutex);
+ dICHECK(lock_result == EOK || ((errno = lock_result), false));
+
+ if (!m_signaled_state)
+ {
+ if (!timeout_time_ptr || timeout_time_ptr->wait_nsec != 0 || timeout_time_ptr->wait_sec != 0)
+ {
+ wait_result = BlockAsAWaiter(timeout_time_ptr);
+ }
+ else
+ {
+ wait_result = false;
+ }
+ }
+ else
+ {
+ m_signaled_state = m_state_is_permanent;
+ wait_result = true;
+ }
+
+ int unlock_result = pthread_mutex_unlock(&m_wakeup_mutex);
+ dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
+
+ return wait_result;
+}
+
+bool dxCondvarWakeup::BlockAsAWaiter(const dThreadedWaitTime *timeout_time_ptr)
+{
+ bool wait_result = false;
+
+ dxWaiterInfo waiter_info;
+ RegisterWaiterInList(&waiter_info);
+
+ timespec wakeup_time;
+
+ if (timeout_time_ptr != NULL)
+ {
+ timespec current_time;
+
+ int clock_result = _condvar_clock_gettime(CLOCK_MONOTONIC, ¤t_time);
+ dICHECK(clock_result != -1);
+
+ time_t wakeup_sec = current_time.tv_sec + timeout_time_ptr->wait_sec;
+ unsigned long wakeup_nsec = current_time.tv_nsec + timeout_time_ptr->wait_nsec;
+
+ if (wakeup_nsec >= 1000000000)
+ {
+ wakeup_nsec -= 1000000000;
+ wakeup_sec += 1;
+ }
+
+ wakeup_time.tv_sec = wakeup_sec;
+ wakeup_time.tv_nsec = wakeup_nsec;
+ }
+
+ while (true)
+ {
+ int cond_result = (timeout_time_ptr != NULL)
+ ? pthread_cond_timedwait(&m_wakeup_cond, &m_wakeup_mutex, &wakeup_time)
+ : pthread_cond_wait(&m_wakeup_cond, &m_wakeup_mutex);
+ dICHECK(cond_result == EOK || cond_result == ETIMEDOUT || ((errno = cond_result), false));
+
+ if (waiter_info.m_signal_state)
+ {
+ wait_result = true;
+ break;
+ }
+
+ if (cond_result == ETIMEDOUT)
+ {
+ dIASSERT(timeout_time_ptr != NULL);
+ break;
+ }
+ }
+
+ UnregisterWaiterFromList(&waiter_info);
+
+ return wait_result;
+}
+
+
+void dxCondvarWakeup::RegisterWaiterInList(dxWaiterInfo *waiter_info)
+{
+ dxWaiterInfo *const first_waiter = m_waiters_list;
+
+ if (first_waiter == NULL)
+ {
+ waiter_info->m_next_info = waiter_info;
+ waiter_info->m_prev_info_ptr = &waiter_info->m_next_info;
+ m_waiters_list = waiter_info;
+ }
+ else
+ {
+ waiter_info->m_next_info = first_waiter;
+ waiter_info->m_prev_info_ptr = first_waiter->m_prev_info_ptr;
+ *first_waiter->m_prev_info_ptr = waiter_info;
+ first_waiter->m_prev_info_ptr = &waiter_info->m_next_info;
+ }
+}
+
+void dxCondvarWakeup::UnregisterWaiterFromList(dxWaiterInfo *waiter_info)
+{
+ dxWaiterInfo *next_info = waiter_info->m_next_info;
+
+ if (next_info == waiter_info)
+ {
+ m_waiters_list = NULL;
+ }
+ else
+ {
+ next_info->m_prev_info_ptr = waiter_info->m_prev_info_ptr;
+ *waiter_info->m_prev_info_ptr = next_info;
+
+ if (waiter_info == m_waiters_list)
+ {
+ m_waiters_list = next_info;
+ }
+ }
+}
+
+
+bool dxCondvarWakeup::MarkSignaledFirstWaiter()
+{
+ bool waiter_found = false;
+
+ dxWaiterInfo *const first_waiter = m_waiters_list;
+
+ if (first_waiter)
+ {
+ waiter_found = MarkSignaledFirstWaiterMeaningful(first_waiter);
+ }
+
+ return waiter_found;
+}
+
+bool dxCondvarWakeup::MarkSignaledFirstWaiterMeaningful(dxWaiterInfo *first_waiter)
+{
+ bool waiter_found = false;
+
+ dxWaiterInfo *current_waiter = first_waiter;
+
+ while (true)
+ {
+ if (!current_waiter->m_signal_state)
+ {
+ current_waiter->m_signal_state = true;
+ waiter_found = true;
+ break;
+ }
+
+ current_waiter = current_waiter->m_next_info;
+ if (current_waiter == first_waiter)
+ {
+ break;
+ }
+ }
+
+ return waiter_found;
+}
+
+bool dxCondvarWakeup::MarkSignaledAllWaiters()
+{
+ bool waiter_found = false;
+
+ dxWaiterInfo *const first_waiter = m_waiters_list;
+
+ if (first_waiter)
+ {
+ waiter_found = MarkSignaledAllWaitersMeaningful(first_waiter);
+ }
+
+ return waiter_found;
+}
+
+bool dxCondvarWakeup::MarkSignaledAllWaitersMeaningful(dxWaiterInfo *first_waiter)
+{
+ bool waiter_found = false;
+
+ dxWaiterInfo *current_waiter = first_waiter;
+
+ while (true)
+ {
+ if (!current_waiter->m_signal_state)
+ {
+ current_waiter->m_signal_state = true;
+ waiter_found = true;
+ }
+
+ current_waiter = current_waiter->m_next_info;
+ if (current_waiter == first_waiter)
+ {
+ break;
+ }
+ }
+
+ return waiter_found;
+}
+
+
+/************************************************************************/
+/* dxMutexMutex class implementation */
+/************************************************************************/
+
+class dxMutexMutex
+{
+public:
+ dxMutexMutex(): m_mutex_allocated(false) {}
+ ~dxMutexMutex() { DoFinalizeObject(); }
+
+ bool InitializeObject() { return DoInitializeObject(); }
+
+private:
+ bool DoInitializeObject();
+ void DoFinalizeObject();
+
+public:
+ void LockMutex();
+ bool TryLockMutex();
+ void UnlockMutex();
+
+private:
+ pthread_mutex_t m_mutex_instance;
+ bool m_mutex_allocated;
+};
+
+
+bool dxMutexMutex::DoInitializeObject()
+{
+ dIASSERT(!m_mutex_allocated);
+
+ bool init_result = false;
+
+ do
+ {
+ int mutex_result = pthread_mutex_init(&m_mutex_instance, NULL);
+ if (mutex_result != EOK)
+ {
+ errno = mutex_result;
+ break;
+ }
+
+ m_mutex_allocated = true;
+ init_result = true;
+ }
+ while (false);
+
+ return init_result;
+}
+
+void dxMutexMutex::DoFinalizeObject()
+{
+ if (m_mutex_allocated)
+ {
+ int mutex_result = pthread_mutex_destroy(&m_mutex_instance);
+ dICHECK(mutex_result == EOK || ((errno = mutex_result), false));
+
+ m_mutex_allocated = false;
+ }
+}
+
+
+void dxMutexMutex::LockMutex()
+{
+ int lock_result = pthread_mutex_lock(&m_mutex_instance);
+ dICHECK(lock_result == EOK || ((errno = lock_result), false));
+}
+
+bool dxMutexMutex::TryLockMutex()
+{
+ int trylock_result = pthread_mutex_trylock(&m_mutex_instance);
+ dICHECK(trylock_result == EOK || trylock_result == EBUSY || ((errno = trylock_result), false));
+
+ return trylock_result == EOK;
+}
+
+void dxMutexMutex::UnlockMutex()
+{
+ int unlock_result = pthread_mutex_unlock(&m_mutex_instance);
+ dICHECK(unlock_result == EOK || ((errno = unlock_result), false));
+}
+
+
+#endif // #if dBUILTIN_THREADING_IMPL_ENABLED
+
+
+/************************************************************************/
+/* Self-threaded job list definition */
+/************************************************************************/
+
+typedef dxtemplateJobListContainer<dxFakeLull, dxFakeMutex, dxFakeAtomicsProvider> dxSelfThreadedJobListContainer;
+typedef dxtemplateJobListSelfHandler<dxSelfWakeup, dxSelfThreadedJobListContainer> dxSelfThreadedJobListHandler;
+typedef dxtemplateThreadingImplementation<dxSelfThreadedJobListContainer, dxSelfThreadedJobListHandler> dxSelfThreadedThreading;
+
+
+#if dBUILTIN_THREADING_IMPL_ENABLED
+
+/************************************************************************/
+/* Multi-threaded job list definition */
+/************************************************************************/
+
+typedef dxtemplateJobListContainer<dxtemplateThreadedLull<dxCondvarWakeup, dxOUAtomicsProvider, false>, dxMutexMutex, dxOUAtomicsProvider> dxMultiThreadedJobListContainer;
+typedef dxtemplateJobListThreadedHandler<dxCondvarWakeup, dxMultiThreadedJobListContainer> dxMultiThreadedJobListHandler;
+typedef dxtemplateThreadingImplementation<dxMultiThreadedJobListContainer, dxMultiThreadedJobListHandler> dxMultiThreadedThreading;
+
+
+#endif // #if dBUILTIN_THREADING_IMPL_ENABLED
+
+
+#endif // #if !defined(_WIN32)
+
+
+#endif // #ifndef _ODE_THREADING_IMPL_POSIX_H_
|