ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/gclib/GThreads.cpp
Revision: 310
Committed: Fri Mar 22 20:06:27 2013 UTC (5 years, 10 months ago) by gpertea
File size: 11466 byte(s)
Log Message:
sync with igm repo

Line File contents
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 */