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,2006 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 5723 2021-03-13 09:18:00Z arthurcnorman $ *
23 ********************************************************************************/
24 #ifdef WIN32
25 #if _WIN32_WINNT < 0x0400
26 #define _WIN32_WINNT 0x0400
27 #endif
28 #endif
29 #include "xincs.h"
30 #include "fxver.h"
31 #include "fxdefs.h"
32 #include "FXThread.h"
33 #ifndef WIN32
34 #include <sys/time.h>
35 #ifdef __APPLE__
36 #ifdef Status
37 #undef Status
38 #endif
39 #ifdef KeyClass
40 #undef KeyClass
41 #endif
42 #include <CoreServices/CoreServices.h>
43 #include <pthread.h>
44 #else
45 #include <pthread.h>
46 #include <semaphore.h>
47 #endif
48 #else
49 #include <process.h>
50 #endif
51
52
53 /*
54 Notes:
55
56 - We have a amorphous blob of memory reserved for the mutex implementation.
57 Since we're trying to avoid having to include platform-specific headers
58 in application code, we can't easily know how much to allocate for
59 pthread_mutex_t [or CRITICAL_SECTION].
60
61 - We don't want to allocate dynamically because of the performance
62 issues, and also because obviously, since heap memory is shared between
63 threads, a malloc itself involves locking another mutex, leaving a
64 potential for an unexpected deadlock.
65
66 - So we just reserve some memory which we will hope to be enough. If it
67 ever turns out its not, the assert should trigger and we'll just have
68 to change the source a bit.
69
70 - If you run into this, try to figure out sizeof(pthread_mutex_t) and
71 let me know about it (jeroen@fox-toolkit.org).
72
73 - I do recommend running this in debug mode first time around on a
74 new platform.
75
76 - Picked unsigned long so as to ensure alignment issues are taken
77 care off.
78
79 - I now believe its safe to set tid=0 after run returns; if FXThread
80 is destroyed then the execution is stopped immediately; if the thread
81 exits, tid is also set to 0. If the thread is cancelled, tid is also
82 set to 0. In no circumstance I can see is it possible for run() to
83 return when FXThread no longer exists.
84 */
85
86 using namespace FX;
87
88
89 namespace FX {
90
91 /*******************************************************************************/
92
93 // Unix implementation
94
95 #ifndef WIN32
96
97
98 // Initialize mutex
FXMutex(FXbool recursive)99 FXMutex::FXMutex(FXbool recursive){
100 pthread_mutexattr_t mutexatt;
101 // If this fails on your machine, determine what value
102 // of sizeof(pthread_mutex_t) is supposed to be on your
103 // machine and mail it to: jeroen@fox-toolkit.org!!
104 //FXTRACE((150,"sizeof(pthread_mutex_t)=%d\n",sizeof(pthread_mutex_t)));
105 FXASSERT(sizeof(data)>=sizeof(pthread_mutex_t));
106 pthread_mutexattr_init(&mutexatt);
107 pthread_mutexattr_settype(&mutexatt,recursive?PTHREAD_MUTEX_RECURSIVE:PTHREAD_MUTEX_DEFAULT);
108 pthread_mutex_init((pthread_mutex_t*)data,&mutexatt);
109 pthread_mutexattr_destroy(&mutexatt);
110 }
111
112
113 // Lock the mutex
lock()114 void FXMutex::lock(){
115 pthread_mutex_lock((pthread_mutex_t*)data);
116 }
117
118
119 // Try lock the mutex
trylock()120 FXbool FXMutex::trylock(){
121 return pthread_mutex_trylock((pthread_mutex_t*)data)==0;
122 }
123
124
125 // Unlock mutex
unlock()126 void FXMutex::unlock(){
127 pthread_mutex_unlock((pthread_mutex_t*)data);
128 }
129
130
131 // Test if locked
locked()132 FXbool FXMutex::locked(){
133 if(pthread_mutex_trylock((pthread_mutex_t*)data)==0){
134 pthread_mutex_unlock((pthread_mutex_t*)data);
135 return false;
136 }
137 return true;
138 }
139
140
141 // Delete mutex
~FXMutex()142 FXMutex::~FXMutex(){
143 pthread_mutex_destroy((pthread_mutex_t*)data);
144 }
145
146
147 /*******************************************************************************/
148
149
150 #ifdef __APPLE__
151
152
153 // Initialize semaphore
FXSemaphore(FXint initial)154 FXSemaphore::FXSemaphore(FXint initial){
155 // If this fails on your machine, determine what value
156 // of sizeof(MPSemaphoreID*) is supposed to be on your
157 // machine and mail it to: jeroen@fox-toolkit.org!!
158 //FXTRACE((150,"sizeof(MPSemaphoreID*)=%d\n",sizeof(MPSemaphoreID*)));
159 FXASSERT(sizeof(data)>=sizeof(MPSemaphoreID*));
160 MPCreateSemaphore(2147483647,initial,(MPSemaphoreID*)data);
161 }
162
163
164 // Decrement semaphore
wait()165 void FXSemaphore::wait(){
166 MPWaitOnSemaphore(*((MPSemaphoreID*)data),kDurationForever);
167 }
168
169
170 // Decrement semaphore but don't block
trywait()171 FXbool FXSemaphore::trywait(){
172 return MPWaitOnSemaphore(*((MPSemaphoreID*)data),kDurationImmediate)==noErr;
173 }
174
175
176 // Increment semaphore
post()177 void FXSemaphore::post(){
178 MPSignalSemaphore(*((MPSemaphoreID*)data));
179 }
180
181
182 // Delete semaphore
~FXSemaphore()183 FXSemaphore::~FXSemaphore(){
184 MPDeleteSemaphore(*((MPSemaphoreID*)data));
185 }
186
187 #else
188
189 // Initialize semaphore
FXSemaphore(FXint initial)190 FXSemaphore::FXSemaphore(FXint initial){
191 // If this fails on your machine, determine what value
192 // of sizeof(sem_t) is supposed to be on your
193 // machine and mail it to: jeroen@fox-toolkit.org!!
194 //FXTRACE((150,"sizeof(sem_t)=%d\n",sizeof(sem_t)));
195 FXASSERT(sizeof(data)>=sizeof(sem_t));
196 sem_init((sem_t*)data,0,(unsigned int)initial);
197 }
198
199
200 // Decrement semaphore
wait()201 void FXSemaphore::wait(){
202 sem_wait((sem_t*)data);
203 }
204
205
206 // Decrement semaphore but don't block
trywait()207 FXbool FXSemaphore::trywait(){
208 return sem_trywait((sem_t*)data)==0;
209 }
210
211
212 // Increment semaphore
post()213 void FXSemaphore::post(){
214 sem_post((sem_t*)data);
215 }
216
217
218 // Delete semaphore
~FXSemaphore()219 FXSemaphore::~FXSemaphore(){
220 sem_destroy((sem_t*)data);
221 }
222
223 #endif
224
225 /*******************************************************************************/
226
227
228 // Initialize condition
FXCondition()229 FXCondition::FXCondition(){
230 // If this fails on your machine, determine what value
231 // of sizeof(pthread_cond_t) is supposed to be on your
232 // machine and mail it to: jeroen@fox-toolkit.org!!
233 //FXTRACE((150,"sizeof(pthread_cond_t)=%d\n",sizeof(pthread_cond_t)));
234 FXASSERT(sizeof(data)>=sizeof(pthread_cond_t));
235 pthread_cond_init((pthread_cond_t*)data,NULL);
236 }
237
238
239 // Wake up one single waiting thread
signal()240 void FXCondition::signal(){
241 pthread_cond_signal((pthread_cond_t*)data);
242 }
243
244
245 // Wake up all waiting threads
broadcast()246 void FXCondition::broadcast(){
247 pthread_cond_broadcast((pthread_cond_t*)data);
248 }
249
250
251 // Wait for condition indefinitely
wait(FXMutex & mtx)252 void FXCondition::wait(FXMutex& mtx){
253 pthread_cond_wait((pthread_cond_t*)data,(pthread_mutex_t*)mtx.data);
254 }
255
256
257 // Wait for condition but fall through after timeout
wait(FXMutex & mtx,FXlong nsec)258 FXbool FXCondition::wait(FXMutex& mtx,FXlong nsec){
259 int result;
260 struct timespec ts;
261 ts.tv_sec=nsec/1000000000;
262 ts.tv_nsec=nsec%1000000000;
263 x:result=pthread_cond_timedwait((pthread_cond_t*)data,(pthread_mutex_t*)mtx.data,&ts);
264 if(result==EINTR) goto x;
265 return result!=ETIMEDOUT;
266 }
267
268
269 // Delete condition
~FXCondition()270 FXCondition::~FXCondition(){
271 pthread_cond_destroy((pthread_cond_t*)data);
272 }
273
274
275 /*******************************************************************************/
276
277 // Thread local storage key for back-reference to FXThread
278 static pthread_key_t self_key;
279
280 // Global initializer for the self_key variable
281 struct TLSKEYINIT {
TLSKEYINITFX::TLSKEYINIT282 TLSKEYINIT(){ pthread_key_create(&self_key,NULL); }
~TLSKEYINITFX::TLSKEYINIT283 ~TLSKEYINIT(){ pthread_key_delete(self_key); }
284 };
285
286
287 // Extern declaration prevents overzealous optimizer from noticing we're
288 // never using this object, and subsequently eliminating it from the code.
289 extern TLSKEYINIT initializer;
290
291 // Dummy object causes global initializer to run
292 TLSKEYINIT initializer;
293
294
295 // Initialize thread
FXThread()296 FXThread::FXThread():tid(0){
297 }
298
299
300 // Return thread id of this thread object.
301 // Purposefully NOT inlined, the tid may be changed by another
302 // thread and therefore we must force the compiler to fetch
303 // this value fresh each time it is needed!
id() const304 FXThreadID FXThread::id() const {
305 return tid;
306 }
307
308
309 // Return TRUE if this thread is running
running() const310 FXbool FXThread::running() const {
311 return tid!=0;
312 }
313
314
315 // Start the thread; we associate the FXThread instance with
316 // this thread using thread-local storage accessed with self_key.
317 // Also, we catch any errors thrown by the thread code here.
execute(void * thread)318 void* FXThread::execute(void* thread){
319 FXint code=-1;
320 pthread_setspecific(self_key,thread);
321 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
322 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
323 try{ code=((FXThread*)thread)->run(); } catch(...){ }
324 ((FXThread*)thread)->tid=0;
325 return (void*)(FXival)code;
326 }
327
328
329 // Start thread; make sure that stacksize >= PTHREAD_STACK_MIN.
330 // We can't check for it because not all machines have this the
331 // PTHREAD_STACK_MIN definition.
start(unsigned long stacksize)332 FXbool FXThread::start(unsigned long stacksize){
333 FXbool code;
334 pthread_attr_t attr;
335 pthread_attr_init(&attr);
336 pthread_attr_setinheritsched(&attr,PTHREAD_INHERIT_SCHED);
337 if(stacksize){ pthread_attr_setstacksize(&attr,stacksize); }
338 //pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
339 code=pthread_create((pthread_t*)&tid,&attr,FXThread::execute,(void*)this)==0;
340 pthread_attr_destroy(&attr);
341 return code;
342 }
343
344
345 // Suspend calling thread until thread is done
join(FXint & code)346 FXbool FXThread::join(FXint& code){
347 pthread_t ttid=(pthread_t)tid;
348 void *trc=NULL;
349 if(ttid && pthread_join(ttid,&trc)==0){
350 code=(FXint)(FXival)trc;
351 tid=0;
352 return TRUE;
353 }
354 return FALSE;
355 }
356
357
358 // Suspend calling thread until thread is done
join()359 FXbool FXThread::join(){
360 pthread_t ttid=(pthread_t)tid;
361 if(ttid && pthread_join(ttid,NULL)==0){
362 tid=0;
363 return TRUE;
364 }
365 return FALSE;
366 }
367
368
369 // Cancel the thread
cancel()370 FXbool FXThread::cancel(){
371 pthread_t ttid=(pthread_t)tid;
372 if(ttid && pthread_cancel(ttid)==0){
373 pthread_join(ttid,NULL);
374 tid=0;
375 return TRUE;
376 }
377 return FALSE;
378 }
379
380
381 // Detach thread
detach()382 FXbool FXThread::detach(){
383 pthread_t ttid=(pthread_t)tid;
384 return ttid && pthread_detach(ttid)==0;
385 }
386
387
388 // Exit calling thread
exit(FXint code)389 void FXThread::exit(FXint code){
390 if(self()){ self()->tid=0; }
391 pthread_exit((void*)(FXival)code);
392 }
393
394
395 // Yield the thread
yield()396 void FXThread::yield(){
397 sched_yield(); // More portable than pthread_yield()
398 }
399
400
401 // Get time in nanoseconds since Epoch
time()402 FXlong FXThread::time(){
403 #ifdef __USE_POSIX199309
404 const FXlong seconds=1000000000;
405 struct timespec ts;
406 clock_gettime(CLOCK_REALTIME,&ts);
407 return ts.tv_sec*seconds+ts.tv_nsec;
408 #else
409 const FXlong seconds=1000000000;
410 const FXlong microseconds=1000;
411 struct timeval tv;
412 gettimeofday(&tv,NULL);
413 return tv.tv_sec*seconds+tv.tv_usec*microseconds;
414 #endif
415 }
416
417
418 // Sleep for some time
sleep(FXlong nsec)419 void FXThread::sleep(FXlong nsec){
420 #ifdef __USE_POSIX199309
421 const FXlong seconds=1000000000;
422 struct timespec value;
423 value.tv_sec=nsec/seconds;
424 value.tv_nsec=nsec%seconds;
425 nanosleep(&value,NULL);
426 #else
427 const FXlong seconds=1000000000;
428 const FXlong microseconds=1000;
429 const FXlong milliseconds=1000000;
430 struct timeval value;
431 value.tv_usec=(nsec/microseconds)%milliseconds;
432 value.tv_sec=nsec/seconds;
433 select(1,0,0,0,&value);
434 #endif
435 }
436
437
438 // Wake at appointed time
wakeat(FXlong nsec)439 void FXThread::wakeat(FXlong nsec){
440 #ifdef __USE_POSIX199309
441 const FXlong seconds=1000000000;
442 struct timespec value;
443 #ifdef __USE_XOPEN2K
444 value.tv_sec=nsec/seconds;
445 value.tv_nsec=nsec%seconds;
446 clock_nanosleep(CLOCK_REALTIME,TIMER_ABSTIME,&value,NULL);
447 #else
448 nsec-=FXThread::time();
449 if(nsec<0) nsec=0;
450 value.tv_sec=nsec/seconds;
451 value.tv_nsec=nsec%seconds;
452 nanosleep(&value,NULL);
453 #endif
454 #else
455 const FXlong seconds=1000000000;
456 const FXlong microseconds=1000;
457 const FXlong milliseconds=1000000;
458 struct timeval value;
459 if(nsec<0) nsec=0;
460 value.tv_usec=(nsec/microseconds)%milliseconds;
461 value.tv_sec=nsec/seconds;
462 select(1,0,0,0,&value);
463 #endif
464 }
465
466
467 // Return pointer to calling thread's instance
self()468 FXThread* FXThread::self(){
469 return (FXThread*)pthread_getspecific(self_key);
470 }
471
472
473 // Return thread id of caller
current()474 FXThreadID FXThread::current(){
475 return (FXThreadID)pthread_self();
476 }
477
478
479 // Set thread priority
priority(FXint prio)480 void FXThread::priority(FXint prio){
481 pthread_t ttid=(pthread_t)tid;
482 if(ttid){
483 sched_param sched={0};
484 int pcy=0;
485 pthread_getschedparam(ttid,&pcy,&sched);
486 #if defined(_POSIX_PRIORITY_SCHEDULING)
487 int priomax=sched_get_priority_max(pcy);
488 int priomin=sched_get_priority_min(pcy);
489 sched.sched_priority=FXCLAMP(priomin,prio,priomax);
490 #elif defined(PTHREAD_MINPRIORITY)
491 sched.sched_priority=FXCLAMP(PTHREAD_MIN_PRIORITY,prio,PTHREAD_MAX_PRIORITY);
492 #endif
493 pthread_setschedparam(ttid,pcy,&sched);
494 }
495 }
496
497
498 // Return thread priority
priority()499 FXint FXThread::priority(){
500 pthread_t ttid=(pthread_t)tid;
501 if(ttid){
502 sched_param sched={0};
503 int pcy=0;
504 pthread_getschedparam(ttid,&pcy,&sched);
505 return sched.sched_priority;
506 }
507 return 0;
508 }
509
510
511 // Destroy; if it was running, stop it
~FXThread()512 FXThread::~FXThread(){
513 pthread_t ttid=(pthread_t)tid;
514 if(ttid){
515 pthread_cancel(ttid);
516 }
517 }
518
519
520 /*******************************************************************************/
521
522 // Windows implementation
523
524 #else
525
526 // Initialize mutex
527 FXMutex::FXMutex(FXbool){
528 // If this fails on your machine, determine what value
529 // of sizeof(CRITICAL_SECTION) is supposed to be on your
530 // machine and mail it to: jeroen@fox-toolkit.org!!
531 //FXTRACE((150,"sizeof(CRITICAL_SECTION)=%d\n",sizeof(CRITICAL_SECTION)));
532 FXASSERT(sizeof(data)>=sizeof(CRITICAL_SECTION));
533 InitializeCriticalSection((CRITICAL_SECTION*)data);
534 }
535
536
537 // Lock the mutex
538 void FXMutex::lock(){
539 EnterCriticalSection((CRITICAL_SECTION*)data);
540 }
541
542
543
544 // Try lock the mutex
545 FXbool FXMutex::trylock(){
546 #if(_WIN32_WINNT >= 0x0400)
547 return TryEnterCriticalSection((CRITICAL_SECTION*)data)!=0;
548 #else
549 return FALSE;
550 #endif
551 }
552
553
554 // Unlock mutex
555 void FXMutex::unlock(){
556 LeaveCriticalSection((CRITICAL_SECTION*)data);
557 }
558
559
560 // Test if locked
561 FXbool FXMutex::locked(){
562 #if(_WIN32_WINNT >= 0x0400)
563 if(TryEnterCriticalSection((CRITICAL_SECTION*)data)!=0){
564 LeaveCriticalSection((CRITICAL_SECTION*)data);
565 return false;
566 }
567 #endif
568 return true;
569 }
570
571
572 // Delete mutex
573 FXMutex::~FXMutex(){
574 DeleteCriticalSection((CRITICAL_SECTION*)data);
575 }
576
577
578 /*******************************************************************************/
579
580
581 // Initialize semaphore
582 FXSemaphore::FXSemaphore(FXint initial){
583 data[0]=(FXuval)CreateSemaphore(NULL,initial,0x7fffffff,NULL);
584 }
585
586
587 // Decrement semaphore
588 void FXSemaphore::wait(){
589 WaitForSingleObject((HANDLE)data[0],INFINITE);
590 }
591
592
593 // Non-blocking semaphore decrement
594 FXbool FXSemaphore::trywait(){
595 return WaitForSingleObject((HANDLE)data[0],0)==WAIT_OBJECT_0;
596 }
597
598
599 // Increment semaphore
600 void FXSemaphore::post(){
601 ReleaseSemaphore((HANDLE)data[0],1,NULL);
602 }
603
604
605 // Delete semaphore
606 FXSemaphore::~FXSemaphore(){
607 CloseHandle((HANDLE)data[0]);
608 }
609
610
611 /*******************************************************************************/
612
613
614 // This is the solution according to Schmidt, the win32-threads
615 // implementation thereof which is found inside GCC somewhere.
616 // See: (http://www.cs.wustl.edu/~schmidt/win32-cv-1.html).
617 //
618 // Our implementation however initializes the Event objects in
619 // the constructor, under the assumption that you wouldn't be creating
620 // a condition object if you weren't planning to use them somewhere.
621
622
623 // Initialize condition
624 FXCondition::FXCondition(){
625 // If this fails on your machine, notify jeroen@fox-toolkit.org!
626 FXASSERT(sizeof(data)>=sizeof(CRITICAL_SECTION)+sizeof(HANDLE)+sizeof(HANDLE)+sizeof(FXuval));
627 data[0]=(FXuval)CreateEvent(NULL,0,0,NULL); // Wakes one, autoreset
628 data[1]=(FXuval)CreateEvent(NULL,1,0,NULL); // Wakes all, manual reset
629 data[2]=0; // Blocked count
630 InitializeCriticalSection((CRITICAL_SECTION*)&data[3]); // Critical section
631 }
632
633
634 // Wake up one single waiting thread
635 void FXCondition::signal(){
636 EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
637 int blocked=(data[2]>0);
638 LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
639 if(blocked) SetEvent((HANDLE)data[0]);
640 }
641
642
643 // Wake up all waiting threads
644 void FXCondition::broadcast(){
645 EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
646 int blocked=(data[2]>0);
647 LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
648 if(blocked) SetEvent((HANDLE)data[1]);
649 }
650
651
652 // Wait
653 void FXCondition::wait(FXMutex& mtx){
654 EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
655 data[2]++;
656 LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
657 mtx.unlock();
658 DWORD result=WaitForMultipleObjects(2,(HANDLE*)data,0,INFINITE);
659 EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
660 data[2]--;
661 int last_waiter=(result==WAIT_OBJECT_0+1)&&(data[2]==0); // Unblocked by broadcast & no other blocked threads
662 LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
663 if(last_waiter) ResetEvent((HANDLE)data[1]); // Reset signal
664 mtx.lock();
665 }
666
667
668 // Wait using single global mutex
669 FXbool FXCondition::wait(FXMutex& mtx,FXlong nsec){
670 EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
671 data[2]++;
672 LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
673 mtx.unlock();
674 nsec-=FXThread::time();
675 DWORD result=WaitForMultipleObjects(2,(HANDLE*)data,0,nsec/1000000);
676 EnterCriticalSection((CRITICAL_SECTION*)&data[3]);
677 data[2]--;
678 int last_waiter=(result==WAIT_OBJECT_0+1)&&(data[2]==0); // Unblocked by broadcast & no other blocked threads
679 LeaveCriticalSection((CRITICAL_SECTION*)&data[3]);
680 if(last_waiter) ResetEvent((HANDLE)data[1]); // Reset signal
681 mtx.lock();
682 return result!=WAIT_TIMEOUT;
683 }
684
685
686 // Delete condition
687 FXCondition::~FXCondition(){
688 CloseHandle((HANDLE)data[0]);
689 CloseHandle((HANDLE)data[1]);
690 DeleteCriticalSection((CRITICAL_SECTION*)&data[3]);
691 }
692
693
694 /*******************************************************************************/
695
696 // Thread local storage key for back-reference to FXThread
697 static DWORD self_key=0xffffffff;
698
699 // Global initializer for the self_key variable
700 struct TLSKEYINIT {
701 TLSKEYINIT(){ self_key=TlsAlloc(); }
702 ~TLSKEYINIT(){ TlsFree(self_key); }
703 };
704
705 // Extern declaration prevents overzealous optimizer from noticing we're
706 // never using this object, and subsequently eliminating it from the code.
707 extern TLSKEYINIT initializer;
708
709 // Dummy object causes global initializer to run
710 TLSKEYINIT initializer;
711
712
713 // Initialize thread
714 FXThread::FXThread():tid(0){
715 }
716
717
718 // Return thread id of this thread object.
719 // Purposefully NOT inlined, the tid may be changed by another
720 // thread and therefore we must force the compiler to fetch
721 // this value fresh each time it is needed!
722 FXThreadID FXThread::id() const {
723 return tid;
724 }
725
726
727 // Return TRUE if this thread is running
728 FXbool FXThread::running() const {
729 return tid!=0;
730 }
731
732
733 // Start the thread; we associate the FXThread instance with
734 // this thread using thread-local storage accessed with self_key.
735 // Also, we catch any errors thrown by the thread code here.
736 unsigned int CALLBACK FXThread::execute(void* thread){
737 FXint code=-1;
738 TlsSetValue(self_key,thread);
739 try{ code=((FXThread*)thread)->run(); } catch(...){ }
740 ((FXThread*)thread)->tid=0;
741 return code;
742 }
743
744
745 // Start thread
746 FXbool FXThread::start(unsigned long stacksize){
747 DWORD thd;
748 tid=(FXThreadID)CreateThread(NULL,stacksize,(LPTHREAD_START_ROUTINE)FXThread::execute,this,0,&thd);
749 return tid!=NULL;
750 }
751
752
753 // Suspend calling thread until thread is done
754 FXbool FXThread::join(FXint& code){
755 HANDLE ttid=(HANDLE)tid;
756 if(ttid && WaitForSingleObject(ttid,INFINITE)==WAIT_OBJECT_0){
757 GetExitCodeThread(ttid,(DWORD*)&code);
758 CloseHandle(ttid);
759 tid=0;
760 return TRUE;
761 }
762 return FALSE;
763 }
764
765
766 // Suspend calling thread until thread is done
767 FXbool FXThread::join(){
768 HANDLE ttid=(HANDLE)tid;
769 if(ttid && WaitForSingleObject(ttid,INFINITE)==WAIT_OBJECT_0){
770 CloseHandle(ttid);
771 tid=0;
772 return TRUE;
773 }
774 return FALSE;
775 }
776
777
778 // Cancel the thread
779 FXbool FXThread::cancel(){
780 HANDLE ttid=(HANDLE)tid;
781 if(ttid && TerminateThread(ttid,0)){
782 CloseHandle(ttid);
783 tid=0;
784 return TRUE;
785 }
786 return FALSE;
787 }
788
789
790 // Detach thread
791 FXbool FXThread::detach(){
792 return tid!=0;
793 }
794
795
796 // Exit calling thread
797 void FXThread::exit(FXint code){
798 if(self()){ self()->tid=0; }
799 ExitThread(code);
800 }
801
802
803 // Yield the thread
804 void FXThread::yield(){
805 Sleep(0);
806 }
807
808
809 // Get time in nanoseconds since Epoch
810 FXlong FXThread::time(){
811 FXlong now;
812 GetSystemTimeAsFileTime((FILETIME*)&now);
813 #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__SC__)
814 return (now-116444736000000000LL)*100LL;
815 #else
816 return (now-116444736000000000L)*100L;
817 #endif
818 }
819
820
821 // Sleep for some time
822 void FXThread::sleep(FXlong nsec){
823 Sleep(nsec/1000000);
824 }
825
826
827 // Wake at appointed time
828 void FXThread::wakeat(FXlong nsec){
829 nsec-=FXThread::time();
830 if(nsec<0) nsec=0;
831 Sleep(nsec/1000000);
832 }
833
834
835 // Return thread id of caller
836 FXThreadID FXThread::current(){
837 return (FXThreadID)GetCurrentThreadId();
838 }
839
840
841 // Return pointer to calling thread's instance
842 FXThread* FXThread::self(){
843 return (FXThread*)TlsGetValue(self_key);
844 }
845
846
847 // Set thread priority
848 void FXThread::priority(FXint prio){
849 HANDLE ttid=(HANDLE)tid;
850 if(ttid){
851 SetThreadPriority(ttid,prio);
852 }
853 }
854
855
856 // Return thread priority
857 FXint FXThread::priority(){
858 HANDLE ttid=(HANDLE)tid;
859 if(ttid){
860 return GetThreadPriority(ttid);
861 }
862 return 0;
863 }
864
865
866 // Destroy
867 FXThread::~FXThread(){
868 HANDLE ttid=(HANDLE)tid;
869 if(ttid){
870 TerminateThread(ttid,0);
871 CloseHandle(ttid);
872 }
873 }
874
875
876 #endif
877
878
879 }
880