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