summaryrefslogtreecommitdiff
path: root/lib/os/os_thread.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/os/os_thread.hpp')
-rw-r--r--lib/os/os_thread.hpp539
1 files changed, 539 insertions, 0 deletions
diff --git a/lib/os/os_thread.hpp b/lib/os/os_thread.hpp
new file mode 100644
index 00000000..fa080b91
--- /dev/null
+++ b/lib/os/os_thread.hpp
@@ -0,0 +1,539 @@
+/**************************************************************************
+ *
+ * Copyright 2011-2012 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * OS native thread abstraction.
+ *
+ * Mimics/leverages C++11 threads.
+ */
+
+#pragma once
+
+
+/* XXX: We still use our own implementation:
+ *
+ * - MSVC's C++11 threads implementation are hardcoded to use C++ exceptions
+ *
+ * - MinGW's C++11 threads implementation is often either missing or relies on
+ * winpthreads
+ */
+
+#if !defined(_WIN32)
+#define HAVE_CXX11_THREADS
+#endif
+
+
+#ifdef HAVE_CXX11_THREADS
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+namespace os {
+
+ using std::mutex;
+ using std::recursive_mutex;
+ using std::unique_lock;
+ using std::condition_variable;
+ using std::thread;
+
+} /* namespace os */
+
+
+#else /* !HAVE_CXX11_THREADS */
+
+
+#include <assert.h>
+#ifdef _WIN32
+# include <process.h>
+# include <windows.h>
+# if _WIN32_WINNT >= 0x0600
+# define HAVE_WIN32_CONDITION_VARIABLES
+# endif
+#else
+# include <pthread.h>
+# include <unistd.h>
+#endif
+
+#include <functional>
+
+
+namespace os {
+
+
+ /**
+ * Base class for mutex and recursive_mutex.
+ */
+ class _base_mutex
+ {
+ public:
+#ifdef _WIN32
+ typedef CRITICAL_SECTION native_handle_type;
+#else
+ typedef pthread_mutex_t native_handle_type;
+#endif
+
+ protected:
+ _base_mutex(void) {
+ }
+
+ public:
+ ~_base_mutex() {
+#ifdef _WIN32
+ DeleteCriticalSection(&_native_handle);
+#else
+ pthread_mutex_destroy(&_native_handle);
+#endif
+ }
+
+ inline void
+ lock(void) {
+#ifdef _WIN32
+ EnterCriticalSection(&_native_handle);
+#else
+ pthread_mutex_lock(&_native_handle);
+#endif
+ }
+
+ inline void
+ unlock(void) {
+#ifdef _WIN32
+ LeaveCriticalSection(&_native_handle);
+#else
+ pthread_mutex_unlock(&_native_handle);
+#endif
+ }
+
+ native_handle_type & native_handle() {
+ return _native_handle;
+ }
+
+ protected:
+ native_handle_type _native_handle;
+ };
+
+
+ /**
+ * Same interface as std::mutex.
+ */
+ class mutex : public _base_mutex
+ {
+ public:
+ inline
+ mutex(void) {
+#ifdef _WIN32
+ InitializeCriticalSection(&_native_handle);
+#else
+ pthread_mutex_init(&_native_handle, NULL);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::recursive_mutex.
+ */
+ class recursive_mutex : public _base_mutex
+ {
+ public:
+ inline
+ recursive_mutex(void) {
+#ifdef _WIN32
+ InitializeCriticalSection(&_native_handle);
+#else
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&_native_handle, &attr);
+ pthread_mutexattr_destroy(&attr);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::unique_lock;
+ */
+ template< class Mutex >
+ class unique_lock
+ {
+ public:
+ typedef Mutex mutex_type;
+
+ inline explicit
+ unique_lock(mutex_type &m) :
+ _mutex(m)
+ {
+ _mutex.lock();
+ }
+
+ inline
+ ~unique_lock() {
+ _mutex.unlock();
+ }
+
+ inline void
+ lock() {
+ _mutex.lock();
+ }
+
+ inline void
+ unlock() {
+ _mutex.unlock();
+ }
+
+ mutex_type *
+ mutex() const {
+ return &_mutex;
+ }
+
+ protected:
+ mutex_type &_mutex;
+ };
+
+
+ /**
+ * Same interface as std::condition_variable
+ */
+ class condition_variable
+ {
+ private:
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ // Only supported on Vista an higher. Not yet supported by WINE.
+ typedef CONDITION_VARIABLE native_handle_type;
+ native_handle_type _native_handle;
+#else
+ // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+ LONG cWaiters;
+ enum {
+ EVENT_ONE = 0,
+ EVENT_ALL,
+ EVENT_COUNT
+ };
+ HANDLE hEvents[EVENT_COUNT];
+#endif
+#else
+ typedef pthread_cond_t native_handle_type;
+ native_handle_type _native_handle;
+#endif
+
+ public:
+ condition_variable() {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ InitializeConditionVariable(&_native_handle);
+# else
+ cWaiters = 0;
+ hEvents[EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ hEvents[EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
+# endif
+#else
+ pthread_cond_init(&_native_handle, NULL);
+#endif
+ }
+
+ ~condition_variable() {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ /* No-op */
+# else
+ CloseHandle(hEvents[EVENT_ALL]);
+ CloseHandle(hEvents[EVENT_ONE]);
+# endif
+#else
+ pthread_cond_destroy(&_native_handle);
+#endif
+ }
+
+ inline void
+ notify_one(void) {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ WakeConditionVariable(&_native_handle);
+# else
+ if (cWaiters) {
+ SetEvent(hEvents[EVENT_ONE]);
+ }
+# endif
+#else
+ pthread_cond_signal(&_native_handle);
+#endif
+ }
+
+ inline void
+ notify_all(void) {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ WakeAllConditionVariable(&_native_handle);
+# else
+ if (cWaiters) {
+ SetEvent(hEvents[EVENT_ALL]);
+ }
+# endif
+#else
+ pthread_cond_broadcast(&_native_handle);
+#endif
+ }
+
+ inline void
+ wait(unique_lock<mutex> & lock) {
+ mutex::native_handle_type & mutex_native_handle = lock.mutex()->native_handle();
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ SleepConditionVariableCS(&_native_handle, &mutex_native_handle, INFINITE);
+# else
+ InterlockedIncrement(&cWaiters);
+ LeaveCriticalSection(&mutex_native_handle);
+ DWORD dwResult;
+ dwResult = WaitForMultipleObjects(EVENT_COUNT, hEvents, FALSE, INFINITE);
+ EnterCriticalSection(&mutex_native_handle);
+ if (InterlockedDecrement(&cWaiters) == 0 &&
+ dwResult == WAIT_OBJECT_0 + EVENT_ALL) {
+ ResetEvent(hEvents[EVENT_ALL]);
+ }
+# endif
+#else
+ pthread_cond_wait(&_native_handle, &mutex_native_handle);
+#endif
+ }
+
+ inline void
+ wait(unique_lock<mutex> & lock, std::function<bool()> pred) {
+ while (!pred) {
+ wait(lock);
+ }
+ }
+ };
+
+
+ /**
+ * Implement TLS through OS threading API.
+ *
+ * This will only work when T is a pointer, intptr_t, or uintptr_t.
+ */
+ template <typename T>
+ class thread_specific
+ {
+ private:
+ static_assert(sizeof(T) == sizeof(void *), "Size mismatch");
+
+#ifdef _WIN32
+ DWORD dwTlsIndex;
+#else
+ pthread_key_t key;
+#endif
+
+ public:
+ thread_specific(void) {
+#ifdef _WIN32
+ dwTlsIndex = TlsAlloc();
+#else
+ pthread_key_create(&key, NULL);
+#endif
+ }
+
+ ~thread_specific() {
+#ifdef _WIN32
+ TlsFree(dwTlsIndex);
+#else
+ pthread_key_delete(key);
+#endif
+ }
+
+ inline T
+ get(void) const {
+ void *ptr;
+#ifdef _WIN32
+ ptr = TlsGetValue(dwTlsIndex);
+#else
+ ptr = pthread_getspecific(key);
+#endif
+ return reinterpret_cast<T>(ptr);
+ }
+
+ inline
+ operator T (void) const
+ {
+ return get();
+ }
+
+ inline T
+ operator -> (void) const
+ {
+ return get();
+ }
+
+ inline T
+ operator = (T new_value)
+ {
+ set(new_value);
+ return new_value;
+ }
+
+ inline void
+ set(T new_value) {
+ void *new_ptr = reinterpret_cast<void *>(new_value);
+#ifdef _WIN32
+ TlsSetValue(dwTlsIndex, new_ptr);
+#else
+ pthread_setspecific(key, new_ptr);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::thread
+ */
+ class thread {
+ public:
+#ifdef _WIN32
+ typedef HANDLE native_handle_type;
+#else
+ typedef pthread_t native_handle_type;
+#endif
+
+ inline
+ thread() :
+ _native_handle(0)
+ {
+ }
+
+ inline
+ thread(thread &&other) :
+ _native_handle(other._native_handle)
+ {
+ other._native_handle = 0;
+ }
+
+ thread(const thread &other) = delete;
+
+ inline
+ ~thread() {
+ }
+
+ static unsigned
+ hardware_concurrency(void) {
+#ifdef _WIN32
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwNumberOfProcessors;
+#else
+ return sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+ }
+
+ template< class Function, class... Args >
+ explicit thread(Function &&f, Args&&... args) {
+ auto bound = std::bind(std::forward<Function>(f), std::forward<Args>(args)...);
+ auto data = new decltype(bound) (std::move(bound));
+ _native_handle = _create(data);
+ }
+
+ inline thread &
+ operator =(thread &&other) {
+ assert(_native_handle == 0);
+ _native_handle = other._native_handle;
+ other._native_handle = 0;
+ return *this;
+ }
+
+ inline bool
+ joinable(void) const {
+ return _native_handle != 0;
+ }
+
+ inline void
+ join() {
+#ifdef _WIN32
+ WaitForSingleObject(_native_handle, INFINITE);
+#else
+ pthread_join(_native_handle, NULL);
+#endif
+ }
+
+ private:
+ native_handle_type _native_handle;
+
+ template< typename Param >
+ static
+#ifdef _WIN32
+ unsigned __stdcall
+#else
+ void *
+#endif
+ _callback(void *lpParameter) {
+ Param *pParam = static_cast<Param *>(lpParameter);
+ (*pParam)();
+ delete pParam;
+ return 0;
+ }
+
+ template< typename Param >
+ static inline native_handle_type
+ _create(Param *function) {
+#ifdef _WIN32
+ uintptr_t handle =_beginthreadex(NULL, 0, &_callback<Param>, function, 0, NULL);
+ return reinterpret_cast<HANDLE>(handle);
+#else
+ pthread_t t;
+ pthread_create(&t, NULL, &_callback<Param>, function);
+ return t;
+#endif
+ }
+ };
+
+} /* namespace os */
+
+
+#endif /* !HAVE_CXX11_THREADS */
+
+
+/**
+ * Compiler TLS.
+ *
+ * It's not portable to use for DLLs on Windows XP, or non-POD types.
+ *
+ * See also:
+ * - http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Thread_002dLocal.html
+ * - http://msdn.microsoft.com/en-us/library/9w1sdazb.aspx
+ * - https://msdn.microsoft.com/en-us/library/y5f6w579.aspx
+ */
+#if defined(__GNUC__)
+# define OS_THREAD_LOCAL __thread
+#elif defined(_MSC_VER)
+# define OS_THREAD_LOCAL __declspec(thread)
+#else
+# error
+#endif
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
+# define OS_THREAD_SPECIFIC(_type) os::thread_specific< _type >
+#else
+# define OS_THREAD_SPECIFIC(_type) OS_THREAD_LOCAL _type
+#endif
+
+#define OS_THREAD_SPECIFIC_PTR(_type) OS_THREAD_SPECIFIC(_type *)