1 |
/* |
2 |
Copyright (c) 2010 Marcus Geelnard |
3 |
(with minor modifications by Geo Pertea) |
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 "GThreads.h" |
25 |
|
26 |
#if defined(_GTHREADS_POSIX_) |
27 |
#include <unistd.h> |
28 |
// #include <map> |
29 |
#elif defined(_GTHREADS_WIN32_) |
30 |
#include <process.h> |
31 |
#endif |
32 |
|
33 |
|
34 |
//namespace tthread { |
35 |
|
36 |
//------------------------------------------------------------------------------ |
37 |
// condition_variable |
38 |
//------------------------------------------------------------------------------ |
39 |
// NOTE 1: The Win32 implementation of the condition_variable class is based on |
40 |
// the corresponding implementation in GLFW, which in turn is based on a |
41 |
// description by Douglas C. Schmidt and Irfan Pyarali: |
42 |
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html |
43 |
// |
44 |
// NOTE 2: Windows Vista actually has native support for condition variables |
45 |
// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to |
46 |
// be portable with pre-Vista Windows versions, so TinyThread++ does not use |
47 |
// Vista condition variables. |
48 |
//------------------------------------------------------------------------------ |
49 |
|
50 |
#if defined(_GTHREADS_WIN32_) |
51 |
#define _CONDITION_EVENT_ONE 0 |
52 |
#define _CONDITION_EVENT_ALL 1 |
53 |
#endif |
54 |
|
55 |
|
56 |
int GThread::tcounter=0; |
57 |
int GThread::num_created=0; |
58 |
|
59 |
#if defined(_GTHREADS_WIN32_) |
60 |
GConditionVar::GConditionVar() : mWaitersCount(0) |
61 |
{ |
62 |
mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); |
63 |
mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); |
64 |
InitializeCriticalSection(&mWaitersCountLock); |
65 |
} |
66 |
#endif |
67 |
|
68 |
#if defined(_GTHREADS_WIN32_) |
69 |
GConditionVar::~GConditionVar() |
70 |
{ |
71 |
CloseHandle(mEvents[_CONDITION_EVENT_ONE]); |
72 |
CloseHandle(mEvents[_CONDITION_EVENT_ALL]); |
73 |
DeleteCriticalSection(&mWaitersCountLock); |
74 |
} |
75 |
#endif |
76 |
|
77 |
#if defined(_GTHREADS_WIN32_) |
78 |
void GConditionVar::_wait() |
79 |
{ |
80 |
// Wait for either event to become signaled due to notify_one() or |
81 |
// notify_all() being called |
82 |
int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); |
83 |
|
84 |
// Check if we are the last waiter |
85 |
EnterCriticalSection(&mWaitersCountLock); |
86 |
-- mWaitersCount; |
87 |
bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && |
88 |
(mWaitersCount == 0); |
89 |
LeaveCriticalSection(&mWaitersCountLock); |
90 |
|
91 |
// If we are the last waiter to be notified to stop waiting, reset the event |
92 |
if(lastWaiter) |
93 |
ResetEvent(mEvents[_CONDITION_EVENT_ALL]); |
94 |
} |
95 |
#endif |
96 |
|
97 |
#if defined(_GTHREADS_WIN32_) |
98 |
void GConditionVar::notify_one() |
99 |
{ |
100 |
// Are there any waiters? |
101 |
EnterCriticalSection(&mWaitersCountLock); |
102 |
bool haveWaiters = (mWaitersCount > 0); |
103 |
LeaveCriticalSection(&mWaitersCountLock); |
104 |
|
105 |
// If we have any waiting threads, send them a signal |
106 |
if(haveWaiters) |
107 |
SetEvent(mEvents[_CONDITION_EVENT_ONE]); |
108 |
} |
109 |
#endif |
110 |
|
111 |
#if defined(_GTHREADS_WIN32_) |
112 |
void GConditionVar::notify_all() |
113 |
{ |
114 |
// Are there any waiters? |
115 |
EnterCriticalSection(&mWaitersCountLock); |
116 |
bool haveWaiters = (mWaitersCount > 0); |
117 |
LeaveCriticalSection(&mWaitersCountLock); |
118 |
|
119 |
// If we have any waiting threads, send them a signal |
120 |
if(haveWaiters) |
121 |
SetEvent(mEvents[_CONDITION_EVENT_ALL]); |
122 |
} |
123 |
#endif |
124 |
|
125 |
|
126 |
//------------------------------------------------------------------------------ |
127 |
// POSIX pthread_t to unique thread::id mapping logic. |
128 |
// Note: Here we use a global thread safe std::map to convert instances of |
129 |
// pthread_t to small thread identifier numbers (unique within one process). |
130 |
// This method should be portable across different POSIX implementations. |
131 |
//------------------------------------------------------------------------------ |
132 |
/* |
133 |
|
134 |
#if defined(_GTHREADS_POSIX_) |
135 |
static thread::id _pthread_t_to_ID(const pthread_t &aHandle) |
136 |
{ |
137 |
static mutex idMapLock; |
138 |
static std::map<pthread_t, unsigned long int> idMap; |
139 |
static unsigned long int idCount(1); |
140 |
|
141 |
lock_guard<mutex> guard(idMapLock); |
142 |
if(idMap.find(aHandle) == idMap.end()) |
143 |
idMap[aHandle] = idCount ++; |
144 |
return thread::id(idMap[aHandle]); |
145 |
} |
146 |
#endif // _GTHREADS_POSIX_ |
147 |
*/ |
148 |
|
149 |
void GThread::update_counter(int inc, GThread* t_update) { |
150 |
static GMutex counterLock; |
151 |
GLockGuard<GMutex> guard(counterLock); |
152 |
if (inc==1) { //joinable thread creation |
153 |
GThread::num_created++; |
154 |
t_update->mId = GThread::num_created; |
155 |
} |
156 |
GThread::tcounter+=inc; |
157 |
if (t_update!=NULL && inc<0) |
158 |
t_update->mId=0; // thread terminated |
159 |
|
160 |
} |
161 |
|
162 |
|
163 |
//------------------------------------------------------------------------------ |
164 |
// thread |
165 |
//------------------------------------------------------------------------------ |
166 |
|
167 |
/// Information to pass to the new thread (what to run). |
168 |
struct _thread_start_info { |
169 |
/* |
170 |
void * mArg; ///< Function argument for the thread function. |
171 |
GThread * mThread; ///< Pointer to the thread object. |
172 |
*/ |
173 |
GThreadData threadData; |
174 |
//void (*mFunction)(void *, GThread*); |
175 |
void (*mFunction)(void *); ///< Pointer to the function to be executed. |
176 |
void (*gtFunction)(GThreadData&); //custom variant, passing GThreadData |
177 |
//handy constructors: |
178 |
_thread_start_info():threadData() { |
179 |
mFunction=NULL; |
180 |
gtFunction=NULL; |
181 |
} |
182 |
_thread_start_info(GThread* t, void (*aFunc)(void *), void* udata): |
183 |
threadData(udata, t) { |
184 |
mFunction=aFunc; |
185 |
gtFunction=NULL; |
186 |
} |
187 |
_thread_start_info(GThread* t, void (*gtFunc)(GThreadData &), void* udata): |
188 |
threadData(udata, t) { |
189 |
mFunction=NULL; |
190 |
gtFunction=gtFunc; |
191 |
} |
192 |
}; |
193 |
|
194 |
// Thread wrapper function. |
195 |
#if defined(_GTHREADS_WIN32_) |
196 |
unsigned WINAPI GThread::wrapper_function(void * aArg) |
197 |
#elif defined(_GTHREADS_POSIX_) |
198 |
void * GThread::wrapper_function(void * aArg) |
199 |
#endif |
200 |
{ |
201 |
// Get thread startup information |
202 |
_thread_start_info * ti = (_thread_start_info *) aArg; |
203 |
|
204 |
/* |
205 |
try |
206 |
{ |
207 |
// Call the actual client thread function |
208 |
ti->mFunction(ti->mArg, ti->mThread); |
209 |
} |
210 |
catch(...) |
211 |
{ |
212 |
// Uncaught exceptions will terminate the application (default behavior |
213 |
// according to the C++11) |
214 |
std::terminate(); |
215 |
} |
216 |
*/ |
217 |
//ti->mFunction(ti->mArg, ti->mThread); |
218 |
|
219 |
//cheap trick to pass current GThread pointer |
220 |
//when the user doesn't pass anything |
221 |
if (ti->gtFunction) { |
222 |
ti->gtFunction(ti->threadData); |
223 |
} |
224 |
else { |
225 |
if (ti->threadData.udata) { |
226 |
ti->mFunction(ti->threadData.udata); |
227 |
} |
228 |
else { |
229 |
ti->mFunction(ti->threadData.thread); |
230 |
} |
231 |
} |
232 |
// The thread is no longer executing |
233 |
GLockGuard<GMutex> guard(ti->threadData.thread->mDataMutex); |
234 |
ti->threadData.thread->mNotAThread = true; |
235 |
GThread::update_counter(-1, ti->threadData.thread); |
236 |
// The thread is responsible for freeing the startup information |
237 |
delete ti; |
238 |
|
239 |
return 0; |
240 |
} |
241 |
|
242 |
|
243 |
void GThread::initStart(void* tidata) { |
244 |
_thread_start_info * ti = (_thread_start_info *) tidata; |
245 |
/*ti->mFunction = aFunction; |
246 |
ti->mArg = aArg; |
247 |
ti->mThread = this;*/ |
248 |
|
249 |
// The thread is now alive |
250 |
mNotAThread = false; |
251 |
|
252 |
// Create the thread |
253 |
#if defined(_GTHREADS_WIN32_) |
254 |
mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); |
255 |
#elif defined(_GTHREADS_POSIX_) |
256 |
if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) |
257 |
mHandle = 0; |
258 |
#endif |
259 |
|
260 |
// Did we fail to create the thread? |
261 |
if(!mHandle) |
262 |
{ |
263 |
mNotAThread = true; |
264 |
delete ti; |
265 |
} |
266 |
else GThread::update_counter(1, this); |
267 |
} |
268 |
|
269 |
//GThread::GThread(void (*aFunction)(void *, GThread*), void * aArg) |
270 |
GThread::GThread(void (*aFunction)(void *), void * aArg): mId(0), mHandle(0), mNotAThread(true) |
271 |
#if defined(_GTHREADS_WIN32_) |
272 |
, mWin32ThreadID(0) |
273 |
#endif |
274 |
{ |
275 |
kickStart(aFunction, aArg); |
276 |
} |
277 |
|
278 |
void GThread::kickStart(void (*aFunction)(void *), void * aArg) { |
279 |
// Serialize access to this thread structure |
280 |
GLockGuard<GMutex> guard(mDataMutex); |
281 |
// Fill out the thread startup information (passed to the thread wrapper, |
282 |
// which will eventually free it) |
283 |
_thread_start_info * ti = new _thread_start_info(this, aFunction, aArg); |
284 |
initStart(ti); |
285 |
} |
286 |
|
287 |
//custom alternate constructor (non-C++11 compatible), passing GThreadData back to the |
288 |
//user function in order to easily retrieve current GThread object |
289 |
//(better alternative to this_thread) |
290 |
GThread::GThread(void (*gFunction)(GThreadData& thread_data), void * aArg) { |
291 |
kickStart(gFunction, aArg); |
292 |
} |
293 |
|
294 |
void GThread::kickStart(void (*gFunction)(GThreadData& thread_data), void * aArg) { |
295 |
// Serialize access to this thread structure |
296 |
GLockGuard<GMutex> guard(mDataMutex); |
297 |
|
298 |
// Fill out the thread startup information (passed to the thread wrapper, |
299 |
// which will eventually free it) |
300 |
_thread_start_info * ti = new _thread_start_info(this, gFunction, aArg); |
301 |
initStart(ti); |
302 |
} |
303 |
|
304 |
GThread::~GThread() |
305 |
{ |
306 |
if(joinable()) { |
307 |
//std::terminate(); -- why?? |
308 |
GThread::update_counter(-1, this); |
309 |
mDataMutex.lock(); |
310 |
#if defined(_TTHREAD_WIN32_) |
311 |
CloseHandle(mHandle); |
312 |
#elif defined(_TTHREAD_POSIX_) |
313 |
pthread_detach(mHandle); |
314 |
#endif |
315 |
mDataMutex.unlock(); |
316 |
} |
317 |
} |
318 |
|
319 |
void GThread::join() |
320 |
{ |
321 |
if(joinable()) |
322 |
{ |
323 |
#if defined(_GTHREADS_WIN32_) |
324 |
WaitForSingleObject(mHandle, INFINITE); |
325 |
CloseHandle(mHandle); |
326 |
#elif defined(_GTHREADS_POSIX_) |
327 |
pthread_join(mHandle, NULL); |
328 |
#endif |
329 |
} |
330 |
} |
331 |
|
332 |
|
333 |
void GThread::detach() |
334 |
{ |
335 |
mDataMutex.lock(); |
336 |
if(!mNotAThread) |
337 |
{ |
338 |
#if defined(_TTHREAD_WIN32_) |
339 |
CloseHandle(mHandle); |
340 |
#elif defined(_TTHREAD_POSIX_) |
341 |
pthread_detach(mHandle); |
342 |
#endif |
343 |
mNotAThread = true; |
344 |
} |
345 |
mDataMutex.unlock(); |
346 |
} |
347 |
|
348 |
void GThread::wait_all() { |
349 |
while (GThread::num_running()>0) |
350 |
this_thread::sleep_for(chrono::milliseconds(4)); |
351 |
} |
352 |
|
353 |
|
354 |
bool GThread::joinable() const |
355 |
{ |
356 |
mDataMutex.lock(); |
357 |
bool result = !mNotAThread; |
358 |
mDataMutex.unlock(); |
359 |
return result; |
360 |
} |
361 |
|
362 |
int GThread::get_id() const |
363 |
{ |
364 |
if(!joinable()) |
365 |
//return id(); |
366 |
return 0; //FIXME: don't use this |
367 |
else |
368 |
return mId; |
369 |
/* |
370 |
#if defined(_GTHREADS_WIN32_) |
371 |
return id((unsigned long int) mWin32ThreadID); |
372 |
#elif defined(_GTHREADS_POSIX_) |
373 |
return _pthread_t_to_ID(mHandle); |
374 |
#endif |
375 |
*/ |
376 |
} |
377 |
|
378 |
unsigned GThread::hardware_concurrency() |
379 |
{ |
380 |
#if defined(_GTHREADS_WIN32_) |
381 |
SYSTEM_INFO si; |
382 |
GetSystemInfo(&si); |
383 |
return (int) si.dwNumberOfProcessors; |
384 |
#elif defined(_SC_NPROCESSORS_ONLN) |
385 |
return (int) sysconf(_SC_NPROCESSORS_ONLN); |
386 |
#elif defined(_SC_NPROC_ONLN) |
387 |
return (int) sysconf(_SC_NPROC_ONLN); |
388 |
#else |
389 |
// The standard requires this function to return zero if the number of |
390 |
// hardware cores could not be determined. |
391 |
return 0; |
392 |
#endif |
393 |
} |
394 |
|
395 |
|
396 |
//------------------------------------------------------------------------------ |
397 |
// this_thread |
398 |
//------------------------------------------------------------------------------ |
399 |
/* |
400 |
int this_thread::get_id() |
401 |
{ |
402 |
#if defined(_GTHREADS_WIN32_) |
403 |
return thread::id((unsigned long int) GetCurrentThreadId()); |
404 |
#elif defined(_GTHREADS_POSIX_) |
405 |
return _pthread_t_to_ID(pthread_self()); |
406 |
#endif |
407 |
} |
408 |
*/ |