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 |
void (*mFunction)(void *, GThread*); ///< Pointer to the function to be executed. |
170 |
void * mArg; ///< Function argument for the thread function. |
171 |
GThread * mThread; ///< Pointer to the thread object. |
172 |
}; |
173 |
|
174 |
// Thread wrapper function. |
175 |
#if defined(_GTHREADS_WIN32_) |
176 |
unsigned WINAPI GThread::wrapper_function(void * aArg) |
177 |
#elif defined(_GTHREADS_POSIX_) |
178 |
void * GThread::wrapper_function(void * aArg) |
179 |
#endif |
180 |
{ |
181 |
// Get thread startup information |
182 |
_thread_start_info * ti = (_thread_start_info *) aArg; |
183 |
|
184 |
/* |
185 |
try |
186 |
{ |
187 |
// Call the actual client thread function |
188 |
ti->mFunction(ti->mArg, ti->mThread); |
189 |
} |
190 |
catch(...) |
191 |
{ |
192 |
// Uncaught exceptions will terminate the application (default behavior |
193 |
// according to the C++0x draft) |
194 |
std::terminate(); |
195 |
} |
196 |
*/ |
197 |
ti->mFunction(ti->mArg, ti->mThread); |
198 |
|
199 |
// The thread is no longer executing |
200 |
GLockGuard<GMutex> guard(ti->mThread->mDataMutex); |
201 |
ti->mThread->mNotAThread = true; |
202 |
GThread::update_counter(-1, ti->mThread); |
203 |
// The thread is responsible for freeing the startup information |
204 |
delete ti; |
205 |
|
206 |
return 0; |
207 |
} |
208 |
|
209 |
GThread::GThread(void (*aFunction)(void *, GThread*), void * aArg) |
210 |
{ |
211 |
// Serialize access to this thread structure |
212 |
GLockGuard<GMutex> guard(mDataMutex); |
213 |
|
214 |
// Fill out the thread startup information (passed to the thread wrapper, |
215 |
// which will eventually free it) |
216 |
_thread_start_info * ti = new _thread_start_info; |
217 |
ti->mFunction = aFunction; |
218 |
ti->mArg = aArg; |
219 |
ti->mThread = this; |
220 |
|
221 |
// The thread is now alive |
222 |
mNotAThread = false; |
223 |
|
224 |
// Create the thread |
225 |
#if defined(_GTHREADS_WIN32_) |
226 |
mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); |
227 |
#elif defined(_GTHREADS_POSIX_) |
228 |
if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) |
229 |
mHandle = 0; |
230 |
#endif |
231 |
|
232 |
// Did we fail to create the thread? |
233 |
if(!mHandle) |
234 |
{ |
235 |
mNotAThread = true; |
236 |
delete ti; |
237 |
} |
238 |
else GThread::update_counter(1, this); |
239 |
} |
240 |
|
241 |
GThread::~GThread() |
242 |
{ |
243 |
if(joinable()) { |
244 |
//std::terminate(); |
245 |
GThread::update_counter(-1, this); |
246 |
} |
247 |
} |
248 |
|
249 |
void GThread::join() |
250 |
{ |
251 |
if(joinable()) |
252 |
{ |
253 |
#if defined(_GTHREADS_WIN32_) |
254 |
WaitForSingleObject(mHandle, INFINITE); |
255 |
#elif defined(_GTHREADS_POSIX_) |
256 |
pthread_join(mHandle, NULL); |
257 |
#endif |
258 |
} |
259 |
} |
260 |
|
261 |
void GThread::wait_all() { |
262 |
while (GThread::num_running()>0) |
263 |
this_thread::sleep_for(chrono::milliseconds(2)); |
264 |
} |
265 |
|
266 |
|
267 |
bool GThread::joinable() const |
268 |
{ |
269 |
mDataMutex.lock(); |
270 |
bool result = !mNotAThread; |
271 |
mDataMutex.unlock(); |
272 |
return result; |
273 |
} |
274 |
|
275 |
int GThread::get_id() const |
276 |
{ |
277 |
if(!joinable()) |
278 |
//return id(); |
279 |
return 0; //FIXME |
280 |
else |
281 |
return mId; |
282 |
/* |
283 |
#if defined(_GTHREADS_WIN32_) |
284 |
return id((unsigned long int) mWin32ThreadID); |
285 |
#elif defined(_GTHREADS_POSIX_) |
286 |
return _pthread_t_to_ID(mHandle); |
287 |
#endif |
288 |
*/ |
289 |
} |
290 |
|
291 |
unsigned GThread::hardware_concurrency() |
292 |
{ |
293 |
#if defined(_GTHREADS_WIN32_) |
294 |
SYSTEM_INFO si; |
295 |
GetSystemInfo(&si); |
296 |
return (int) si.dwNumberOfProcessors; |
297 |
#elif defined(_SC_NPROCESSORS_ONLN) |
298 |
return (int) sysconf(_SC_NPROCESSORS_ONLN); |
299 |
#elif defined(_SC_NPROC_ONLN) |
300 |
return (int) sysconf(_SC_NPROC_ONLN); |
301 |
#else |
302 |
// The standard requires this function to return zero if the number of |
303 |
// hardware cores could not be determined. |
304 |
return 0; |
305 |
#endif |
306 |
} |
307 |
|
308 |
|
309 |
//------------------------------------------------------------------------------ |
310 |
// this_thread |
311 |
//------------------------------------------------------------------------------ |
312 |
/* |
313 |
int this_thread::get_id() |
314 |
{ |
315 |
#if defined(_GTHREADS_WIN32_) |
316 |
return thread::id((unsigned long int) GetCurrentThreadId()); |
317 |
#elif defined(_GTHREADS_POSIX_) |
318 |
return _pthread_t_to_ID(pthread_self()); |
319 |
#endif |
320 |
} |
321 |
*/ |