1 /**************************************************************************
2  *
3  * Copyright 2011-2012 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25 
26 /*
27  * OS native thread abstraction.
28  *
29  * Mimics/leverages C++11 threads.
30  */
31 
32 #pragma once
33 
34 
35 /* XXX: We still use our own implementation:
36  *
37  * - MSVC's C++11 threads implementation are hardcoded to use C++ exceptions
38  *
39  * - MinGW's C++11 threads implementation is often either missing or relies on
40  *   winpthreads
41  */
42 
43 #if !defined(_WIN32)
44 #define HAVE_CXX11_THREADS
45 #endif
46 
47 
48 #ifdef HAVE_CXX11_THREADS
49 
50 #include <thread>
51 #include <mutex>
52 #include <condition_variable>
53 
54 namespace os {
55 
56     using std::mutex;
57     using std::recursive_mutex;
58     using std::unique_lock;
59     using std::condition_variable;
60     using std::thread;
61 
62 } /* namespace os */
63 
64 
65 #else /* !HAVE_CXX11_THREADS */
66 
67 
68 #include <assert.h>
69 #ifdef _WIN32
70 #  include <process.h>
71 #  include <windows.h>
72 #  if _WIN32_WINNT >= 0x0600
73 #    define HAVE_WIN32_CONDITION_VARIABLES
74 #  endif
75 #else
76 #  include <pthread.h>
77 #  include <unistd.h>
78 #endif
79 
80 #include <functional>
81 
82 
83 namespace os {
84 
85 
86     /**
87      * Base class for mutex and recursive_mutex.
88      */
89     class _base_mutex
90     {
91     public:
92 #ifdef _WIN32
93         typedef CRITICAL_SECTION native_handle_type;
94 #else
95         typedef pthread_mutex_t native_handle_type;
96 #endif
97 
98     protected:
_base_mutex(void)99         _base_mutex(void) {
100         }
101 
102     public:
~_base_mutex()103         ~_base_mutex() {
104 #ifdef _WIN32
105             DeleteCriticalSection(&_native_handle);
106 #else
107             pthread_mutex_destroy(&_native_handle);
108 #endif
109         }
110 
111         inline void
lock(void)112         lock(void) {
113 #ifdef _WIN32
114             EnterCriticalSection(&_native_handle);
115 #else
116             pthread_mutex_lock(&_native_handle);
117 #endif
118         }
119 
120         inline void
unlock(void)121         unlock(void) {
122 #ifdef _WIN32
123             LeaveCriticalSection(&_native_handle);
124 #else
125             pthread_mutex_unlock(&_native_handle);
126 #endif
127         }
128 
native_handle()129         native_handle_type & native_handle() {
130             return _native_handle;
131         }
132 
133     protected:
134         native_handle_type _native_handle;
135     };
136 
137 
138     /**
139      * Same interface as std::mutex.
140      */
141     class mutex : public _base_mutex
142     {
143     public:
144         inline
mutex(void)145         mutex(void) {
146 #ifdef _WIN32
147             InitializeCriticalSection(&_native_handle);
148 #else
149             pthread_mutex_init(&_native_handle, NULL);
150 #endif
151         }
152     };
153 
154 
155     /**
156      * Same interface as std::recursive_mutex.
157      */
158     class recursive_mutex : public _base_mutex
159     {
160     public:
161         inline
recursive_mutex(void)162         recursive_mutex(void) {
163 #ifdef _WIN32
164             InitializeCriticalSection(&_native_handle);
165 #else
166             pthread_mutexattr_t attr;
167             pthread_mutexattr_init(&attr);
168             pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
169             pthread_mutex_init(&_native_handle, &attr);
170             pthread_mutexattr_destroy(&attr);
171 #endif
172         }
173     };
174 
175 
176     /**
177      * Same interface as std::unique_lock;
178      */
179     template< class Mutex >
180     class unique_lock
181     {
182     public:
183         typedef Mutex mutex_type;
184 
185         inline explicit
unique_lock(mutex_type & m)186         unique_lock(mutex_type &m) :
187             _mutex(m)
188         {
189             _mutex.lock();
190         }
191 
192         inline
~unique_lock()193         ~unique_lock() {
194             _mutex.unlock();
195         }
196 
197         inline void
lock()198         lock() {
199             _mutex.lock();
200         }
201 
202         inline void
unlock()203         unlock() {
204             _mutex.unlock();
205         }
206 
207         mutex_type *
mutex() const208         mutex() const {
209             return &_mutex;
210         }
211 
212     protected:
213         mutex_type &_mutex;
214     };
215 
216 
217     /**
218      * Same interface as std::condition_variable
219      */
220     class condition_variable
221     {
222     private:
223 #ifdef _WIN32
224 #  ifdef HAVE_WIN32_CONDITION_VARIABLES
225         // Only supported on Vista an higher. Not yet supported by WINE.
226         typedef CONDITION_VARIABLE native_handle_type;
227         native_handle_type _native_handle;
228 #else
229         // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
230         LONG cWaiters;
231         enum {
232             EVENT_ONE = 0,
233             EVENT_ALL,
234             EVENT_COUNT
235         };
236         HANDLE hEvents[EVENT_COUNT];
237 #endif
238 #else
239         typedef pthread_cond_t native_handle_type;
240         native_handle_type _native_handle;
241 #endif
242 
243     public:
condition_variable()244         condition_variable() {
245 #ifdef _WIN32
246 #  ifdef HAVE_WIN32_CONDITION_VARIABLES
247             InitializeConditionVariable(&_native_handle);
248 #  else
249             cWaiters = 0;
250             hEvents[EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
251             hEvents[EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
252 #  endif
253 #else
254             pthread_cond_init(&_native_handle, NULL);
255 #endif
256         }
257 
~condition_variable()258         ~condition_variable() {
259 #ifdef _WIN32
260 #  ifdef HAVE_WIN32_CONDITION_VARIABLES
261             /* No-op */
262 #  else
263             CloseHandle(hEvents[EVENT_ALL]);
264             CloseHandle(hEvents[EVENT_ONE]);
265 #  endif
266 #else
267             pthread_cond_destroy(&_native_handle);
268 #endif
269         }
270 
271         inline void
notify_one(void)272         notify_one(void) {
273 #ifdef _WIN32
274 #  ifdef HAVE_WIN32_CONDITION_VARIABLES
275             WakeConditionVariable(&_native_handle);
276 #  else
277             if (cWaiters) {
278                 SetEvent(hEvents[EVENT_ONE]);
279             }
280 #  endif
281 #else
282             pthread_cond_signal(&_native_handle);
283 #endif
284         }
285 
286         inline void
notify_all(void)287         notify_all(void) {
288 #ifdef _WIN32
289 #  ifdef HAVE_WIN32_CONDITION_VARIABLES
290             WakeAllConditionVariable(&_native_handle);
291 #  else
292             if (cWaiters) {
293                 SetEvent(hEvents[EVENT_ALL]);
294             }
295 #  endif
296 #else
297             pthread_cond_broadcast(&_native_handle);
298 #endif
299         }
300 
301         inline void
wait(unique_lock<mutex> & lock)302         wait(unique_lock<mutex> & lock) {
303             mutex::native_handle_type & mutex_native_handle = lock.mutex()->native_handle();
304 #ifdef _WIN32
305 #  ifdef HAVE_WIN32_CONDITION_VARIABLES
306             SleepConditionVariableCS(&_native_handle, &mutex_native_handle, INFINITE);
307 #  else
308             InterlockedIncrement(&cWaiters);
309             LeaveCriticalSection(&mutex_native_handle);
310             DWORD dwResult;
311             dwResult = WaitForMultipleObjects(EVENT_COUNT, hEvents, FALSE, INFINITE);
312             EnterCriticalSection(&mutex_native_handle);
313             if (InterlockedDecrement(&cWaiters) == 0 &&
314                 dwResult == WAIT_OBJECT_0 + EVENT_ALL) {
315                 ResetEvent(hEvents[EVENT_ALL]);
316             }
317 #  endif
318 #else
319             pthread_cond_wait(&_native_handle, &mutex_native_handle);
320 #endif
321         }
322 
323         inline void
wait(unique_lock<mutex> & lock,std::function<bool ()> pred)324         wait(unique_lock<mutex> & lock, std::function<bool()> pred) {
325             while (!pred) {
326                 wait(lock);
327             }
328         }
329     };
330 
331 
332     /**
333      * Same interface as std::thread
334      */
335     class thread {
336     public:
337 #ifdef _WIN32
338         typedef HANDLE native_handle_type;
339 #else
340         typedef pthread_t native_handle_type;
341 #endif
342 
343         inline
thread()344         thread() :
345             _native_handle(0)
346         {
347         }
348 
349         inline
thread(thread && other)350         thread(thread &&other) :
351             _native_handle(other._native_handle)
352         {
353             other._native_handle = 0;
354         }
355 
356         thread(const thread &other) = delete;
357 
358         inline
~thread()359         ~thread() {
360         }
361 
362         static unsigned
hardware_concurrency(void)363         hardware_concurrency(void) {
364 #ifdef _WIN32
365             SYSTEM_INFO si;
366             GetSystemInfo(&si);
367             return si.dwNumberOfProcessors;
368 #else
369             return sysconf(_SC_NPROCESSORS_ONLN);
370 #endif
371         }
372 
373         template< class Function, class... Args >
thread(Function && f,Args &&...args)374         explicit thread(Function &&f, Args&&... args) {
375             auto bound = std::bind(std::forward<Function>(f), std::forward<Args>(args)...);
376             auto data = new decltype(bound) (std::move(bound));
377             _native_handle = _create(data);
378         }
379 
380         inline thread &
operator =(thread && other)381         operator =(thread &&other) {
382             assert(_native_handle == 0);
383             _native_handle = other._native_handle;
384             other._native_handle = 0;
385             return *this;
386         }
387 
388         inline bool
joinable(void) const389         joinable(void) const {
390             return _native_handle != 0;
391         }
392 
393         inline void
join()394         join() {
395 #ifdef _WIN32
396             WaitForSingleObject(_native_handle, INFINITE);
397 #else
398             pthread_join(_native_handle, NULL);
399 #endif
400         }
401 
402     private:
403         native_handle_type _native_handle;
404 
405         template< typename Param >
406         static
407 #ifdef _WIN32
408         unsigned __stdcall
409 #else
410         void *
411 #endif
_callback(void * lpParameter)412         _callback(void *lpParameter) {
413             Param *pParam = static_cast<Param *>(lpParameter);
414             (*pParam)();
415             delete pParam;
416             return 0;
417         }
418 
419         template< typename Param >
420         static inline native_handle_type
_create(Param * function)421         _create(Param *function) {
422 #ifdef _WIN32
423             uintptr_t handle =_beginthreadex(NULL, 0, &_callback<Param>, function, 0, NULL);
424             return reinterpret_cast<HANDLE>(handle);
425 #else
426             pthread_t t;
427             pthread_create(&t, NULL, &_callback<Param>, function);
428             return t;
429 #endif
430         }
431     };
432 
433 } /* namespace os */
434 
435 
436 #endif  /* !HAVE_CXX11_THREADS */
437 
438 
439 /**
440  * Compiler TLS.
441  *
442  * It's not portable to use for DLLs on Windows XP, or non-POD types.
443  *
444  * See also:
445  * - http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Thread_002dLocal.html
446  * - http://msdn.microsoft.com/en-us/library/9w1sdazb.aspx
447  * - https://msdn.microsoft.com/en-us/library/y5f6w579.aspx
448  */
449 #if defined(__GNUC__)
450 #  define OS_THREAD_LOCAL __thread
451 #elif defined(_MSC_VER)
452 #  define OS_THREAD_LOCAL __declspec(thread)
453 #else
454 #  error Unsupported C++ compiler
455 #endif
456