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 (6 years, 6 months ago) by gpertea
File size: 11466 byte(s)
Log Message:
sync with igm repo

Line User Rev File contents
1 gpertea 156 /*
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 gpertea 310 /*
170 gpertea 156 void * mArg; ///< Function argument for the thread function.
171     GThread * mThread; ///< Pointer to the thread object.
172 gpertea 310 */
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 gpertea 156 };
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 gpertea 310 // according to the C++11)
214 gpertea 156 std::terminate();
215     }
216     */
217 gpertea 310 //ti->mFunction(ti->mArg, ti->mThread);
218 gpertea 156
219 gpertea 310 //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 gpertea 156 // The thread is no longer executing
233 gpertea 310 GLockGuard<GMutex> guard(ti->threadData.thread->mDataMutex);
234     ti->threadData.thread->mNotAThread = true;
235     GThread::update_counter(-1, ti->threadData.thread);
236 gpertea 156 // The thread is responsible for freeing the startup information
237     delete ti;
238    
239     return 0;
240     }
241    
242    
243 gpertea 310 void GThread::initStart(void* tidata) {
244     _thread_start_info * ti = (_thread_start_info *) tidata;
245     /*ti->mFunction = aFunction;
246 gpertea 156 ti->mArg = aArg;
247 gpertea 310 ti->mThread = this;*/
248 gpertea 156
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 gpertea 310 //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 gpertea 156 GThread::~GThread()
305     {
306     if(joinable()) {
307 gpertea 310 //std::terminate(); -- why??
308 gpertea 156 GThread::update_counter(-1, this);
309 gpertea 310 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 gpertea 156 }
317     }
318    
319     void GThread::join()
320     {
321     if(joinable())
322     {
323     #if defined(_GTHREADS_WIN32_)
324     WaitForSingleObject(mHandle, INFINITE);
325 gpertea 310 CloseHandle(mHandle);
326 gpertea 156 #elif defined(_GTHREADS_POSIX_)
327     pthread_join(mHandle, NULL);
328     #endif
329     }
330     }
331    
332 gpertea 310
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 gpertea 156 void GThread::wait_all() {
349     while (GThread::num_running()>0)
350 gpertea 310 this_thread::sleep_for(chrono::milliseconds(4));
351 gpertea 156 }
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 gpertea 301 return 0; //FIXME: don't use this
367 gpertea 156 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     */