1 /********************************************************************************
2 *                                                                               *
3 *                 M u l i t h r e a d i n g   S u p p o r t                     *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2004,2005 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXThread.cpp,v 1.38.2.3 2005/11/09 10:30:46 fox Exp $                        *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXThread.h"
28 
29 #ifndef WIN32
30 #include <pthread.h>
31 #include <semaphore.h>
32 #else
33 #include <process.h>
34 #endif
35 
36 
37 /*
38   Notes:
39 
40   - We have a amorphous blob of memory reserved for the mutex implementation.
41     Since we're trying to avoid having to include platform-specific headers
42     in application code, we can't easily know how much to allocate for
43     pthread_mutex_t [or CRITICAL_SECTION].
44 
45   - We don't want to allocate dynamically because of the performance
46     issues, and also because obviously, since heap memory is shared between
47     threads, a malloc itself involves locking another mutex, leaving a
48     potential for an unexpected deadlock.
49 
50   - So we just reserve some memory which we will hope to be enough.  If it
51     ever turns out its not, the assert should trigger and we'll just have
52     to change the source a bit.
53 
54   - If you run into this, try to figure out sizeof(pthread_mutex_t) and
55     let me know about it (jeroen@fox-toolkit.org).
56 
57   - I do recommend running this in debug mode first time around on a
58     new platform.
59 
60   - Picked unsigned long so as to ensure alignment issues are taken
61     care off.
62 */
63 
64 using namespace FX;
65 
66 
67 namespace FX {
68 
69 /*******************************************************************************/
70 
71 // Unix implementation
72 
73 #ifndef WIN32
74 
75 
76 // Initialize mutex
FXMutex(FXbool recursive)77 FXMutex::FXMutex(FXbool recursive){
78   pthread_mutexattr_t mutexatt;
79   // If this fails on your machine, determine what value
80   // of sizeof(pthread_mutex_t) is supposed to be on your
81   // machine and mail it to: jeroen@fox-toolkit.org!!
82   //FXTRACE((150,"sizeof(pthread_mutex_t)=%d\n",sizeof(pthread_mutex_t)));
83   FXASSERT(sizeof(data)>=sizeof(pthread_mutex_t));
84   pthread_mutexattr_init(&mutexatt);
85   pthread_mutexattr_settype(&mutexatt,recursive?PTHREAD_MUTEX_RECURSIVE:PTHREAD_MUTEX_DEFAULT);
86   pthread_mutex_init((pthread_mutex_t*)data,&mutexatt);
87   pthread_mutexattr_destroy(&mutexatt);
88   }
89 
90 
91 // Lock the mutex
lock()92 void FXMutex::lock(){
93   pthread_mutex_lock((pthread_mutex_t*)data);
94   }
95 
96 
97 // Try lock the mutex
trylock()98 FXbool FXMutex::trylock(){
99   return pthread_mutex_trylock((pthread_mutex_t*)data)==0;
100   }
101 
102 
103 // Unlock mutex
unlock()104 void FXMutex::unlock(){
105   pthread_mutex_unlock((pthread_mutex_t*)data);
106   }
107 
108 
109 // Test if locked
locked()110 FXbool FXMutex::locked(){
111   if(pthread_mutex_trylock((pthread_mutex_t*)data)==EBUSY) return TRUE;
112   pthread_mutex_unlock((pthread_mutex_t*)data);
113   return FALSE;
114   }
115 
116 
117 // Delete mutex
~FXMutex()118 FXMutex::~FXMutex(){
119   pthread_mutex_destroy((pthread_mutex_t*)data);
120   }
121 
122 
123 /*******************************************************************************/
124 
125 
126 // Initialize semaphore
FXSemaphore(FXint initial)127 FXSemaphore::FXSemaphore(FXint initial){
128   // If this fails on your machine, determine what value
129   // of sizeof(sem_t) is supposed to be on your
130   // machine and mail it to: jeroen@fox-toolkit.org!!
131   //FXTRACE((150,"sizeof(sem_t)=%d\n",sizeof(sem_t)));
132   FXASSERT(sizeof(data)>=sizeof(sem_t));
133   sem_init((sem_t*)data,0,(unsigned int)initial);
134   }
135 
136 
137 // Decrement semaphore
wait()138 void FXSemaphore::wait(){
139   sem_wait((sem_t*)data);
140   }
141 
142 
143 // Decrement semaphore but don't block
trywait()144 FXbool FXSemaphore::trywait(){
145   return sem_trywait((sem_t*)data)==0;
146   }
147 
148 
149 // Increment semaphore
post()150 void FXSemaphore::post(){
151   sem_post((sem_t*)data);
152   }
153 
154 
155 // Delete semaphore
~FXSemaphore()156 FXSemaphore::~FXSemaphore(){
157   sem_destroy((sem_t*)data);
158   }
159 
160 
161 /*******************************************************************************/
162 
163 
164 // Initialize condition
FXCondition()165 FXCondition::FXCondition(){
166   // If this fails on your machine, determine what value
167   // of sizeof(pthread_cond_t) is supposed to be on your
168   // machine and mail it to: jeroen@fox-toolkit.org!!
169   //FXTRACE((150,"sizeof(pthread_cond_t)=%d\n",sizeof(pthread_cond_t)));
170   FXASSERT(sizeof(data)>=sizeof(pthread_cond_t));
171   pthread_cond_init((pthread_cond_t*)data,NULL);
172   }
173 
174 
175 // Wake up one single waiting thread
signal()176 void FXCondition::signal(){
177   pthread_cond_signal((pthread_cond_t*)data);
178   }
179 
180 
181 // Wake up all waiting threads
broadcast()182 void FXCondition::broadcast(){
183   pthread_cond_broadcast((pthread_cond_t*)data);
184   }
185 
186 
187 // Wait for condition indefinitely
wait(FXMutex & mtx)188 void FXCondition::wait(FXMutex& mtx){
189   pthread_cond_wait((pthread_cond_t*)data,(pthread_mutex_t*)&mtx.data);
190   }
191 
192 
193 // Wait for condition but fall through after timeout
wait(FXMutex & mtx,FXuint ms)194 FXbool FXCondition::wait(FXMutex& mtx,FXuint ms){
195   register int result;
196   struct timespec ts;
197   struct timeval tv;
198   gettimeofday(&tv,NULL);
199   ts.tv_nsec=((ms%1000)*1000+tv.tv_usec)*1000;
200   ts.tv_sec=(ms/1000)+(ts.tv_nsec/1000000000)+tv.tv_sec;
201   ts.tv_nsec=ts.tv_nsec%1000000000;
202 x:result=pthread_cond_timedwait((pthread_cond_t*)data,(pthread_mutex_t*)&mtx.data,&ts);
203   if(result==EINTR) goto x;
204   return result!=ETIMEDOUT;
205   }
206 
207 
208 // Delete condition
~FXCondition()209 FXCondition::~FXCondition(){
210   pthread_cond_destroy((pthread_cond_t*)data);
211   }
212 
213 
214 /*******************************************************************************/
215 
216 // Thread local storage key for back-reference to FXThread
217 static pthread_key_t self_key;
218 
219 // Global initializer for the self_key variable
220 struct TLSKEYINIT {
TLSKEYINITFX::TLSKEYINIT221   TLSKEYINIT(){ pthread_key_create(&self_key,NULL); }
~TLSKEYINITFX::TLSKEYINIT222  ~TLSKEYINIT(){ pthread_key_delete(self_key); }
223   };
224 
225 // Extern declaration prevents overzealous optimizer from noticing we're
226 // never using this object, and subsequently eliminating it from the code.
227 extern TLSKEYINIT initializer;
228 
229 // Dummy object causes global initializer to run
230 TLSKEYINIT initializer;
231 
232 
233 // Initialize thread
FXThread()234 FXThread::FXThread():tid(0){
235   }
236 
237 
238 // Return thread id of this thread object.
239 // Purposefully NOT inlined, the tid may be changed by another
240 // thread and therefore we must force the compiler to fetch
241 // this value fresh each time it is needed!
id() const242 FXThreadID FXThread::id() const {
243   return tid;
244   }
245 
246 
247 // Return TRUE if this thread is running
running() const248 FXbool FXThread::running() const {
249   return tid!=0;
250   }
251 
252 
253 // Start the thread; we associate the FXThread instance with
254 // this thread using thread-local storage accessed with self_key.
255 // Also, we catch any errors thrown by the thread code here.
execute(void * thread)256 void* FXThread::execute(void* thread){
257   register FXint code=-1;
258   pthread_setspecific(self_key,thread);
259   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
260   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
261   try{ code=((FXThread*)thread)->run(); } catch(...){ }
262   return (void*)(FXival)code;
263   }
264 
265 
266 // Start thread; make sure that stacksize >= PTHREAD_STACK_MIN.
267 // We can't check for it because not all machines have this the
268 // PTHREAD_STACK_MIN definition.
start(unsigned long stacksize)269 FXbool FXThread::start(unsigned long stacksize){
270   register FXbool code;
271   pthread_attr_t attr;
272   pthread_attr_init(&attr);
273   pthread_attr_setinheritsched(&attr,PTHREAD_INHERIT_SCHED);
274   if(stacksize){ pthread_attr_setstacksize(&attr,stacksize); }
275   //pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
276   code=pthread_create((pthread_t*)&tid,&attr,FXThread::execute,(void*)this)==0;
277   pthread_attr_destroy(&attr);
278   return code;
279   }
280 
281 
282 // Suspend calling thread until thread is done
join(FXint & code)283 FXbool FXThread::join(FXint& code){
284   void *trc=NULL;
285   if(tid && pthread_join((pthread_t)tid,&trc)==0){
286     code=(FXint)(FXival)trc;
287     tid=0;
288     return TRUE;
289     }
290   return FALSE;
291   }
292 
293 
294 // Suspend calling thread until thread is done
join()295 FXbool FXThread::join(){
296   if(tid && pthread_join((pthread_t)tid,NULL)==0){
297     tid=0;
298     return TRUE;
299     }
300   return FALSE;
301   }
302 
303 
304 // Cancel the thread
cancel()305 FXbool FXThread::cancel(){
306   if(tid && pthread_cancel((pthread_t)tid)==0){
307     pthread_join((pthread_t)tid,NULL);
308     tid=0;
309     return TRUE;
310     }
311   return FALSE;
312   }
313 
314 
315 // Detach thread
detach()316 FXbool FXThread::detach(){
317   return tid && pthread_detach((pthread_t)tid)==0;
318   }
319 
320 
321 // Exit calling thread
exit(FXint code)322 void FXThread::exit(FXint code){
323   pthread_exit((void*)(FXival)code);
324   }
325 
326 
327 // Yield the thread
yield()328 void FXThread::yield(){
329   sched_yield();                // More portable than pthread_yield()
330   }
331 
332 
333 // Sleep for some time
sleep(unsigned long sec,unsigned long nsec)334 void FXThread::sleep(unsigned long sec,unsigned long nsec){
335   struct timespec value;
336   value.tv_sec=sec+nsec/1000000000;     // A few seconds is billions and billions of nanoseconds!
337   value.tv_nsec=nsec%1000000000;
338   nanosleep(&value,NULL);
339   }
340 
341 
342 // Return pointer to calling thread's instance
self()343 FXThread* FXThread::self(){
344   return (FXThread*)pthread_getspecific(self_key);
345   }
346 
347 
348 // Return thread id of caller
current()349 FXThreadID FXThread::current(){
350   return (FXThreadID)pthread_self();
351   }
352 
353 
354 // Set thread priority
priority(FXint prio)355 void FXThread::priority(FXint prio){
356   sched_param sched={0};
357   int priomin,priomax;
358   if(tid){
359     priomax=sched_get_priority_max(SCHED_OTHER);
360     priomin=sched_get_priority_min(SCHED_OTHER);
361     sched.sched_priority=FXCLAMP(priomin,prio,priomax);
362     pthread_setschedparam((pthread_t)tid,SCHED_OTHER,&sched);
363     }
364   }
365 
366 
367 // Return thread priority
priority()368 FXint FXThread::priority(){
369   sched_param sched={0};
370   int policy;
371   if(tid){
372     pthread_getschedparam((pthread_t)tid,&policy,&sched);
373     return sched.sched_priority;
374     }
375   return 0;
376   }
377 
378 
379 // Destroy; if it was running, stop it
~FXThread()380 FXThread::~FXThread(){
381   if(tid){ pthread_cancel((pthread_t)tid); }
382   }
383 
384 
385 /*******************************************************************************/
386 
387 // Windows implementation
388 
389 #else
390 
391 // Initialize mutex
392 FXMutex::FXMutex(FXbool){
393   // If this fails on your machine, determine what value
394   // of sizeof(CRITICAL_SECTION) is supposed to be on your
395   // machine and mail it to: jeroen@fox-toolkit.org!!
396   //FXTRACE((150,"sizeof(CRITICAL_SECTION)=%d\n",sizeof(CRITICAL_SECTION)));
397   FXASSERT(sizeof(data)>=sizeof(CRITICAL_SECTION));
398   InitializeCriticalSection((CRITICAL_SECTION*)data);
399   }
400 
401 
402 // Lock the mutex
403 void FXMutex::lock(){
404   EnterCriticalSection((CRITICAL_SECTION*)data);
405   }
406 
407 
408 
409 // Try lock the mutex
410 FXbool FXMutex::trylock(){
411 #if(_WIN32_WINNT >= 0x0400)
412   return TryEnterCriticalSection((CRITICAL_SECTION*)data)!=0;
413 #else
414   return FALSE;
415 #endif
416   }
417 
418 
419 // Unlock mutex
420 void FXMutex::unlock(){
421   LeaveCriticalSection((CRITICAL_SECTION*)data);
422   }
423 
424 
425 // Test if locked
426 FXbool FXMutex::locked(){
427 #if(_WIN32_WINNT >= 0x0400)
428   if(TryEnterCriticalSection((CRITICAL_SECTION*)data)==0) return FALSE;
429   LeaveCriticalSection((CRITICAL_SECTION*)data);
430 #endif
431   return TRUE;
432   }
433 
434 
435 // Delete mutex
436 FXMutex::~FXMutex(){
437   DeleteCriticalSection((CRITICAL_SECTION*)data);
438   }
439 
440 
441 /*******************************************************************************/
442 
443 
444 // Initialize semaphore
445 FXSemaphore::FXSemaphore(FXint initial){
446   data[0]=(FXuval)CreateSemaphore(NULL,initial,0x7fffffff,NULL);
447   }
448 
449 
450 // Decrement semaphore
451 void FXSemaphore::wait(){
452   WaitForSingleObject((HANDLE)data[0],INFINITE);
453   }
454 
455 
456 // Non-blocking semaphore decrement
457 FXbool FXSemaphore::trywait(){
458   return WaitForSingleObject((HANDLE)data[0],0)==WAIT_OBJECT_0;
459   }
460 
461 
462 // Increment semaphore
463 void FXSemaphore::post(){
464   ReleaseSemaphore((HANDLE)data[0],1,NULL);
465   }
466 
467 
468 // Delete semaphore
469 FXSemaphore::~FXSemaphore(){
470   CloseHandle((HANDLE)data[0]);
471   }
472 
473 
474 /*******************************************************************************/
475 
476 
477 // This is the solution according to Schmidt, the win32-threads
478 // implementation thereof which is found inside GCC somewhere.
479 // See: (http://www.cs.wustl.edu/~schmidt/win32-cv-1.html).
480 //
481 // Our implementation however initializes the Event objects in
482 // the constructor, under the assumption that you're not creating
483 // condition object if you weren't planning to use them somewhere.
484 
485 
486 // Initialize condition
487 FXCondition::FXCondition(){
488   // If this fails on your machine, notify jeroen@fox-toolkit.org!
489   FXASSERT(sizeof(data)>=sizeof(CRITICAL_SECTION)+sizeof(HANDLE)+sizeof(HANDLE)+sizeof(FXuval));
490   data[0]=(FXuval)CreateEvent(NULL,0,0,NULL);                   // Wakes one, autoreset
491   data[1]=(FXuval)CreateEvent(NULL,1,0,NULL);                   // Wakes all, manual reset
492   data[2]=0;                                                    // Blocked count
493   InitializeCriticalSection((CRITICAL_SECTION*)&data[3]);       // Critical section
494   }
495 
496 
497 // Wake up one single waiting thread
498 void FXCondition::signal(){
499   EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
500   int blocked=(data[2]>0);
501   LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
502   if(blocked) SetEvent((HANDLE)data[0]);
503   }
504 
505 
506 // Wake up all waiting threads
507 void FXCondition::broadcast(){
508   EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
509   int blocked=(data[2]>0);
510   LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
511   if(blocked) SetEvent((HANDLE)data[1]);
512   }
513 
514 
515 // Wait
516 void FXCondition::wait(FXMutex& mtx){
517   EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
518   data[2]++;
519   LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
520   mtx.unlock();
521   DWORD result=WaitForMultipleObjects(2,(HANDLE*)data,0,INFINITE);
522   EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
523   data[2]--;
524   int last_waiter=(result==WAIT_OBJECT_0+1)&&(data[2]==0);      // Unblocked by broadcast & no other blocked threads
525   LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
526   if(last_waiter) ResetEvent((HANDLE)data[1]);                  // Reset signal
527   mtx.lock();
528   }
529 
530 
531 // Wait using single global mutex
532 FXbool FXCondition::wait(FXMutex& mtx,FXuint ms){
533   EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
534   data[2]++;
535   LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
536   mtx.unlock();
537   DWORD result=WaitForMultipleObjects(2,(HANDLE*)data,0,ms);
538   EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
539   data[2]--;
540   int last_waiter=(result==WAIT_OBJECT_0+1)&&(data[2]==0);      // Unblocked by broadcast & no other blocked threads
541   LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
542   if(last_waiter) ResetEvent((HANDLE)data[1]);                  // Reset signal
543   mtx.lock();
544   return result!=WAIT_TIMEOUT;
545   }
546 
547 
548 // Delete condition
549 FXCondition::~FXCondition(){
550   CloseHandle((HANDLE)data[0]);
551   CloseHandle((HANDLE)data[1]);
552   DeleteCriticalSection((CRITICAL_SECTION*)&data[3]);
553   }
554 
555 
556 /*******************************************************************************/
557 
558 // Thread local storage key for back-reference to FXThread
559 static DWORD self_key=0xffffffff;
560 
561 // Global initializer for the self_key variable
562 struct TLSKEYINIT {
563   TLSKEYINIT(){ self_key=TlsAlloc(); }
564  ~TLSKEYINIT(){ TlsFree(self_key); }
565   };
566 
567 // Extern declaration prevents overzealous optimizer from noticing we're
568 // never using this object, and subsequently eliminating it from the code.
569 extern TLSKEYINIT initializer;
570 
571 // Dummy object causes global initializer to run
572 TLSKEYINIT initializer;
573 
574 
575 // Initialize thread
576 FXThread::FXThread():tid(0){
577   }
578 
579 
580 // Return thread id of this thread object.
581 // Purposefully NOT inlined, the tid may be changed by another
582 // thread and therefore we must force the compiler to fetch
583 // this value fresh each time it is needed!
584 FXThreadID FXThread::id() const {
585   return tid;
586   }
587 
588 
589 // Return TRUE if this thread is running
590 FXbool FXThread::running() const {
591   return tid!=0;
592   }
593 
594 
595 // Start the thread; we associate the FXThread instance with
596 // this thread using thread-local storage accessed with self_key.
597 // Also, we catch any errors thrown by the thread code here.
598 unsigned int CALLBACK FXThread::execute(void* thread){
599   register FXint code=-1;
600   TlsSetValue(self_key,thread);
601   try{ code=((FXThread*)thread)->run(); } catch(...){ }
602   return code;
603   }
604 
605 
606 // Start thread
607 FXbool FXThread::start(unsigned long stacksize){
608   DWORD thd;
609   tid=(FXThreadID)CreateThread(NULL,stacksize,(LPTHREAD_START_ROUTINE)FXThread::execute,this,0,&thd);
610   return tid!=NULL;
611   }
612 
613 
614 // Suspend calling thread until thread is done
615 FXbool FXThread::join(FXint& code){
616   if(tid && WaitForSingleObject((HANDLE)tid,INFINITE)==WAIT_OBJECT_0){
617     GetExitCodeThread((HANDLE)tid,(DWORD*)&code);
618     CloseHandle((HANDLE)tid);
619     tid=0;
620     return TRUE;
621     }
622   return FALSE;
623   }
624 
625 
626 // Suspend calling thread until thread is done
627 FXbool FXThread::join(){
628   if(tid && WaitForSingleObject((HANDLE)tid,INFINITE)==WAIT_OBJECT_0){
629     CloseHandle((HANDLE)tid);
630     tid=0;
631     return TRUE;
632     }
633   return FALSE;
634   }
635 
636 
637 // Cancel the thread
638 FXbool FXThread::cancel(){
639   if(tid && TerminateThread((HANDLE)tid,0)){
640     CloseHandle((HANDLE)tid);
641     tid=0;
642     return TRUE;
643     }
644   return FALSE;
645   }
646 
647 
648 // Detach thread
649 FXbool FXThread::detach(){
650   return tid!=0;
651   }
652 
653 
654 // Exit calling thread
655 void FXThread::exit(FXint code){
656   ExitThread(code);
657   }
658 
659 
660 // Yield the thread
661 void FXThread::yield(){
662   Sleep(0);
663   }
664 
665 
666 // Sleep for some time
667 void FXThread::sleep(unsigned long sec,unsigned long nsec){
668   Sleep(sec*1000+nsec/1000000);
669   }
670 
671 
672 // Return thread id of caller
673 FXThreadID FXThread::current(){
674   return (FXThreadID)GetCurrentThreadId();
675   }
676 
677 
678 // Return pointer to calling thread's instance
679 FXThread* FXThread::self(){
680   return (FXThread*)TlsGetValue(self_key);
681   }
682 
683 
684 // Set thread priority
685 void FXThread::priority(FXint prio){
686   if(tid){
687     SetThreadPriority((HANDLE)tid,prio);
688     }
689   }
690 
691 
692 // Return thread priority
693 FXint FXThread::priority(){
694   if(tid){
695     return GetThreadPriority((HANDLE)tid);
696     }
697   return 0;
698   }
699 
700 
701 // Destroy
702 FXThread::~FXThread(){
703   if(tid){ TerminateThread((HANDLE)tid,0); CloseHandle((HANDLE)tid); }
704   }
705 
706 
707 #endif
708 
709 
710 }
711