1 /* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
2 Copyright (c) 2010-2012 Marcus Geelnard
3 
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11 
12     1. The origin of this software must not be misrepresented; you must not
13     claim that you wrote the original software. If you use this software
14     in a product, an acknowledgment in the product documentation would be
15     appreciated but is not required.
16 
17     2. Altered source versions must be plainly marked as such, and must not be
18     misrepresented as being the original software.
19 
20     3. This notice may not be removed or altered from any source
21     distribution.
22 */
23 
24 #include <exception>
25 #include "tinythread.h"
26 
27 #if defined(_TTHREAD_POSIX_)
28   #include <unistd.h>
29   #include <map>
30 #elif defined(_TTHREAD_WIN32_)
31   #include <process.h>
32 #endif
33 
34 
35 namespace tthread {
36 
37 //------------------------------------------------------------------------------
38 // condition_variable
39 //------------------------------------------------------------------------------
40 // NOTE 1: The Win32 implementation of the condition_variable class is based on
41 // the corresponding implementation in GLFW, which in turn is based on a
42 // description by Douglas C. Schmidt and Irfan Pyarali:
43 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
44 //
45 // NOTE 2: Windows Vista actually has native support for condition variables
46 // (InitializeConditionVariable, WakeConditionVariable, etc), but we want to
47 // be portable with pre-Vista Windows versions, so TinyThread++ does not use
48 // Vista condition variables.
49 //------------------------------------------------------------------------------
50 
51 #if defined(_TTHREAD_WIN32_)
52   #define _CONDITION_EVENT_ONE 0
53   #define _CONDITION_EVENT_ALL 1
54 #endif
55 
56 #if defined(_TTHREAD_WIN32_)
condition_variable()57 condition_variable::condition_variable() : mWaitersCount(0)
58 {
59   mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
60   mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
61   InitializeCriticalSection(&mWaitersCountLock);
62 }
63 #endif
64 
65 #if defined(_TTHREAD_WIN32_)
~condition_variable()66 condition_variable::~condition_variable()
67 {
68   CloseHandle(mEvents[_CONDITION_EVENT_ONE]);
69   CloseHandle(mEvents[_CONDITION_EVENT_ALL]);
70   DeleteCriticalSection(&mWaitersCountLock);
71 }
72 #endif
73 
74 #if defined(_TTHREAD_WIN32_)
_wait()75 void condition_variable::_wait()
76 {
77   // Wait for either event to become signaled due to notify_one() or
78   // notify_all() being called
79   int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE);
80 
81   // Check if we are the last waiter
82   EnterCriticalSection(&mWaitersCountLock);
83   -- mWaitersCount;
84   bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
85                     (mWaitersCount == 0);
86   LeaveCriticalSection(&mWaitersCountLock);
87 
88   // If we are the last waiter to be notified to stop waiting, reset the event
89   if(lastWaiter)
90     ResetEvent(mEvents[_CONDITION_EVENT_ALL]);
91 }
92 #endif
93 
94 #if defined(_TTHREAD_WIN32_)
notify_one()95 void condition_variable::notify_one()
96 {
97   // Are there any waiters?
98   EnterCriticalSection(&mWaitersCountLock);
99   bool haveWaiters = (mWaitersCount > 0);
100   LeaveCriticalSection(&mWaitersCountLock);
101 
102   // If we have any waiting threads, send them a signal
103   if(haveWaiters)
104     SetEvent(mEvents[_CONDITION_EVENT_ONE]);
105 }
106 #endif
107 
108 #if defined(_TTHREAD_WIN32_)
notify_all()109 void condition_variable::notify_all()
110 {
111   // Are there any waiters?
112   EnterCriticalSection(&mWaitersCountLock);
113   bool haveWaiters = (mWaitersCount > 0);
114   LeaveCriticalSection(&mWaitersCountLock);
115 
116   // If we have any waiting threads, send them a signal
117   if(haveWaiters)
118     SetEvent(mEvents[_CONDITION_EVENT_ALL]);
119 }
120 #endif
121 
122 
123 //------------------------------------------------------------------------------
124 // POSIX pthread_t to unique thread::id mapping logic.
125 // Note: Here we use a global thread safe std::map to convert instances of
126 // pthread_t to small thread identifier numbers (unique within one process).
127 // This method should be portable across different POSIX implementations.
128 //------------------------------------------------------------------------------
129 
130 #if defined(_TTHREAD_POSIX_)
_pthread_t_to_ID(const pthread_t & aHandle)131 static thread::id _pthread_t_to_ID(const pthread_t &aHandle)
132 {
133   static mutex idMapLock;
134   static std::map<pthread_t, unsigned long int> idMap;
135   static unsigned long int idCount(1);
136 
137   lock_guard<mutex> guard(idMapLock);
138   if(idMap.find(aHandle) == idMap.end())
139     idMap[aHandle] = idCount ++;
140   return thread::id(idMap[aHandle]);
141 }
142 #endif // _TTHREAD_POSIX_
143 
144 
145 //------------------------------------------------------------------------------
146 // thread
147 //------------------------------------------------------------------------------
148 
149 /// Information shared between the thread wrapper and the thread object.
150 class _thread_wrapper {
151   public:
_thread_wrapper(void (* aFunction)(void *),void * aArg)152     _thread_wrapper(void (*aFunction)(void *), void * aArg) :
153       mFunction(aFunction),
154       mArg(aArg),
155       mRefCount(2)      // Upon creation the object is referenced by two
156                         // instances: the thread object and the thread wrapper
157     {
158     }
159 
run()160     inline void run()
161     {
162       mFunction(mArg);
163     }
164 
joinable() const165     inline bool joinable() const
166     {
167       return mRefCount > 1;
168     }
169 
release()170     inline bool release()
171     {
172       return !(--mRefCount);
173     }
174 
175   private:
176     void (*mFunction)(void *);  // Pointer to the function to be executed
177     void * mArg;                // Function argument for the thread function
178     atomic_int mRefCount;       // Reference count
179 };
180 
181 // Thread wrapper function.
182 #if defined(_TTHREAD_WIN32_)
wrapper_function(void * aArg)183 unsigned WINAPI thread::wrapper_function(void * aArg)
184 #elif defined(_TTHREAD_POSIX_)
185 void * thread::wrapper_function(void * aArg)
186 #endif
187 {
188   // Get thread wrapper information
189   _thread_wrapper * tw = (_thread_wrapper *) aArg;
190 
191   try
192   {
193     // Call the actual client thread function
194     tw->run();
195   }
196   catch(...)
197   {
198     // Uncaught exceptions will terminate the application (default behavior
199     // according to C++11)
200     std::terminate();
201   }
202 
203   // The thread is no longer executing
204   if(tw->release())
205   {
206     delete tw;
207   }
208 
209   return 0;
210 }
211 
thread(void (* aFunction)(void *),void * aArg)212 thread::thread(void (*aFunction)(void *), void * aArg)
213 {
214   // Fill out the thread startup information (passed to the thread wrapper)
215   _thread_wrapper * tw = new _thread_wrapper(aFunction, aArg);
216 
217   // Create the thread
218 #if defined(_TTHREAD_WIN32_)
219   mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) tw, 0, &mWin32ThreadID);
220 #elif defined(_TTHREAD_POSIX_)
221   if(pthread_create(&mHandle, NULL, wrapper_function, (void *) tw) != 0)
222     mHandle = 0;
223 #endif
224 
225   // Did we fail to create the thread?
226   if(!mHandle)
227   {
228     delete tw;
229     tw = 0;
230   }
231 
232   mWrapper = (void *) tw;
233 }
234 
~thread()235 thread::~thread()
236 {
237   _thread_wrapper * tw = static_cast<_thread_wrapper*>(mWrapper);
238   if(!tw)
239     return;
240 
241   if(tw->release())
242   {
243     delete tw;
244   }
245   else
246   {
247     // If the thread wrapper was not released, the thread is still joinable,
248     // which should result in std::terminate() upon destruction according to
249     // spec.
250     std::terminate();
251   }
252 }
253 
join()254 void thread::join()
255 {
256   _thread_wrapper * tw = static_cast<_thread_wrapper*>(mWrapper);
257   if(!tw)
258     return;
259 
260   if(tw->joinable())
261   {
262 #if defined(_TTHREAD_WIN32_)
263     WaitForSingleObject(mHandle, INFINITE);
264     CloseHandle(mHandle);
265 #elif defined(_TTHREAD_POSIX_)
266     pthread_join(mHandle, NULL);
267 #endif
268   }
269 
270   // Note: At this point release() should always return true, since the
271   // wrapper object should already have been released in the thread before
272   // joining.
273   if(tw->release())
274   {
275     delete tw;
276   }
277   mWrapper = 0;
278 }
279 
joinable() const280 bool thread::joinable() const
281 {
282   _thread_wrapper * tw = static_cast<_thread_wrapper*>(mWrapper);
283   if(!tw)
284     return false;
285 
286   return tw->joinable();
287 }
288 
detach()289 void thread::detach()
290 {
291   _thread_wrapper * tw = static_cast<_thread_wrapper*>(mWrapper);
292   if(!tw)
293     return;
294 
295 #if defined(_TTHREAD_WIN32_)
296   CloseHandle(mHandle);
297 #elif defined(_TTHREAD_POSIX_)
298   pthread_detach(mHandle);
299 #endif
300 
301   if(tw->release())
302   {
303     delete tw;
304   }
305   mWrapper = 0;
306 }
307 
get_id() const308 thread::id thread::get_id() const
309 {
310   if(!joinable())
311     return id();
312 #if defined(_TTHREAD_WIN32_)
313   return id((unsigned long int) mWin32ThreadID);
314 #elif defined(_TTHREAD_POSIX_)
315   return _pthread_t_to_ID(mHandle);
316 #endif
317 }
318 
hardware_concurrency()319 unsigned thread::hardware_concurrency()
320 {
321 #if defined(_TTHREAD_WIN32_)
322   SYSTEM_INFO si;
323   GetSystemInfo(&si);
324   return (int) si.dwNumberOfProcessors;
325 #elif defined(_SC_NPROCESSORS_ONLN)
326   return (int) sysconf(_SC_NPROCESSORS_ONLN);
327 #elif defined(_SC_NPROC_ONLN)
328   return (int) sysconf(_SC_NPROC_ONLN);
329 #else
330   // The standard requires this function to return zero if the number of
331   // hardware cores could not be determined.
332   return 0;
333 #endif
334 }
335 
336 
337 //------------------------------------------------------------------------------
338 // this_thread
339 //------------------------------------------------------------------------------
340 
get_id()341 thread::id this_thread::get_id()
342 {
343 #if defined(_TTHREAD_WIN32_)
344   return thread::id((unsigned long int) GetCurrentThreadId());
345 #elif defined(_TTHREAD_POSIX_)
346   return _pthread_t_to_ID(pthread_self());
347 #endif
348 }
349 
350 }
351