1/* Threads compatibility routines for libgcc2 and libobjc. */ 2/* Compile this one with gcc. */ 3 4/* Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 5 Free Software Foundation, Inc. 6 Contributed by Mumit Khan <khan@xraylith.wisc.edu>. 7 8This file is part of GCC. 9 10GCC is free software; you can redistribute it and/or modify it under 11the terms of the GNU General Public License as published by the Free 12Software Foundation; either version 2, or (at your option) any later 13version. 14 15GCC is distributed in the hope that it will be useful, but WITHOUT ANY 16WARRANTY; without even the implied warranty of MERCHANTABILITY or 17FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18for more details. 19 20You should have received a copy of the GNU General Public License 21along with GCC; see the file COPYING. If not, write to the Free 22Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 2302110-1301, USA. */ 24 25/* As a special exception, if you link this library with other files, 26 some of which are compiled with GCC, to produce an executable, 27 this library does not by itself cause the resulting executable 28 to be covered by the GNU General Public License. 29 This exception does not however invalidate any other reasons why 30 the executable file might be covered by the GNU General Public License. */ 31 32#ifndef GCC_GTHR_WIN32_H 33#define GCC_GTHR_WIN32_H 34 35/* Windows32 threads specific definitions. The windows32 threading model 36 does not map well into pthread-inspired gcc's threading model, and so 37 there are caveats one needs to be aware of. 38 39 1. The destructor supplied to __gthread_key_create is ignored for 40 generic x86-win32 ports. This will certainly cause memory leaks 41 due to unreclaimed eh contexts (sizeof (eh_context) is at least 42 24 bytes for x86 currently). 43 44 This memory leak may be significant for long-running applications 45 that make heavy use of C++ EH. 46 47 However, Mingw runtime (version 0.3 or newer) provides a mechanism 48 to emulate pthreads key dtors; the runtime provides a special DLL, 49 linked in if -mthreads option is specified, that runs the dtors in 50 the reverse order of registration when each thread exits. If 51 -mthreads option is not given, a stub is linked in instead of the 52 DLL, which results in memory leak. Other x86-win32 ports can use 53 the same technique of course to avoid the leak. 54 55 2. The error codes returned are non-POSIX like, and cast into ints. 56 This may cause incorrect error return due to truncation values on 57 hw where sizeof (DWORD) > sizeof (int). 58 59 3. We are currently using a special mutex instead of the Critical 60 Sections, since Win9x does not support TryEnterCriticalSection 61 (while NT does). 62 63 The basic framework should work well enough. In the long term, GCC 64 needs to use Structured Exception Handling on Windows32. */ 65 66#define __GTHREADS 1 67 68#include <errno.h> 69#ifdef __MINGW32__ 70#include <_mingw.h> 71#endif 72 73#ifdef _LIBOBJC 74 75/* This is necessary to prevent windef.h (included from windows.h) from 76 defining its own BOOL as a typedef. */ 77#ifndef __OBJC__ 78#define __OBJC__ 79#endif 80#include <windows.h> 81/* Now undef the windows BOOL. */ 82#undef BOOL 83 84/* Key structure for maintaining thread specific storage */ 85static DWORD __gthread_objc_data_tls = (DWORD) -1; 86 87/* Backend initialization functions */ 88 89/* Initialize the threads subsystem. */ 90int 91__gthread_objc_init_thread_system (void) 92{ 93 /* Initialize the thread storage key. */ 94 if ((__gthread_objc_data_tls = TlsAlloc ()) != (DWORD) -1) 95 return 0; 96 else 97 return -1; 98} 99 100/* Close the threads subsystem. */ 101int 102__gthread_objc_close_thread_system (void) 103{ 104 if (__gthread_objc_data_tls != (DWORD) -1) 105 TlsFree (__gthread_objc_data_tls); 106 return 0; 107} 108 109/* Backend thread functions */ 110 111/* Create a new thread of execution. */ 112objc_thread_t 113__gthread_objc_thread_detach (void (*func)(void *arg), void *arg) 114{ 115 DWORD thread_id = 0; 116 HANDLE win32_handle; 117 118 if (!(win32_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) func, 119 arg, 0, &thread_id))) 120 thread_id = 0; 121 122 return (objc_thread_t) thread_id; 123} 124 125/* Set the current thread's priority. */ 126int 127__gthread_objc_thread_set_priority (int priority) 128{ 129 int sys_priority = 0; 130 131 switch (priority) 132 { 133 case OBJC_THREAD_INTERACTIVE_PRIORITY: 134 sys_priority = THREAD_PRIORITY_NORMAL; 135 break; 136 default: 137 case OBJC_THREAD_BACKGROUND_PRIORITY: 138 sys_priority = THREAD_PRIORITY_BELOW_NORMAL; 139 break; 140 case OBJC_THREAD_LOW_PRIORITY: 141 sys_priority = THREAD_PRIORITY_LOWEST; 142 break; 143 } 144 145 /* Change priority */ 146 if (SetThreadPriority (GetCurrentThread (), sys_priority)) 147 return 0; 148 else 149 return -1; 150} 151 152/* Return the current thread's priority. */ 153int 154__gthread_objc_thread_get_priority (void) 155{ 156 int sys_priority; 157 158 sys_priority = GetThreadPriority (GetCurrentThread ()); 159 160 switch (sys_priority) 161 { 162 case THREAD_PRIORITY_HIGHEST: 163 case THREAD_PRIORITY_TIME_CRITICAL: 164 case THREAD_PRIORITY_ABOVE_NORMAL: 165 case THREAD_PRIORITY_NORMAL: 166 return OBJC_THREAD_INTERACTIVE_PRIORITY; 167 168 default: 169 case THREAD_PRIORITY_BELOW_NORMAL: 170 return OBJC_THREAD_BACKGROUND_PRIORITY; 171 172 case THREAD_PRIORITY_IDLE: 173 case THREAD_PRIORITY_LOWEST: 174 return OBJC_THREAD_LOW_PRIORITY; 175 } 176 177 /* Couldn't get priority. */ 178 return -1; 179} 180 181/* Yield our process time to another thread. */ 182void 183__gthread_objc_thread_yield (void) 184{ 185 Sleep (0); 186} 187 188/* Terminate the current thread. */ 189int 190__gthread_objc_thread_exit (void) 191{ 192 /* exit the thread */ 193 ExitThread (__objc_thread_exit_status); 194 195 /* Failed if we reached here */ 196 return -1; 197} 198 199/* Returns an integer value which uniquely describes a thread. */ 200objc_thread_t 201__gthread_objc_thread_id (void) 202{ 203 return (objc_thread_t) GetCurrentThreadId (); 204} 205 206/* Sets the thread's local storage pointer. */ 207int 208__gthread_objc_thread_set_data (void *value) 209{ 210 if (TlsSetValue (__gthread_objc_data_tls, value)) 211 return 0; 212 else 213 return -1; 214} 215 216/* Returns the thread's local storage pointer. */ 217void * 218__gthread_objc_thread_get_data (void) 219{ 220 DWORD lasterror; 221 void *ptr; 222 223 lasterror = GetLastError (); 224 225 ptr = TlsGetValue (__gthread_objc_data_tls); /* Return thread data. */ 226 227 SetLastError (lasterror); 228 229 return ptr; 230} 231 232/* Backend mutex functions */ 233 234/* Allocate a mutex. */ 235int 236__gthread_objc_mutex_allocate (objc_mutex_t mutex) 237{ 238 if ((mutex->backend = (void *) CreateMutex (NULL, 0, NULL)) == NULL) 239 return -1; 240 else 241 return 0; 242} 243 244/* Deallocate a mutex. */ 245int 246__gthread_objc_mutex_deallocate (objc_mutex_t mutex) 247{ 248 CloseHandle ((HANDLE) (mutex->backend)); 249 return 0; 250} 251 252/* Grab a lock on a mutex. */ 253int 254__gthread_objc_mutex_lock (objc_mutex_t mutex) 255{ 256 int status; 257 258 status = WaitForSingleObject ((HANDLE) (mutex->backend), INFINITE); 259 if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED) 260 return -1; 261 else 262 return 0; 263} 264 265/* Try to grab a lock on a mutex. */ 266int 267__gthread_objc_mutex_trylock (objc_mutex_t mutex) 268{ 269 int status; 270 271 status = WaitForSingleObject ((HANDLE) (mutex->backend), 0); 272 if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED) 273 return -1; 274 else 275 return 0; 276} 277 278/* Unlock the mutex */ 279int 280__gthread_objc_mutex_unlock (objc_mutex_t mutex) 281{ 282 if (ReleaseMutex ((HANDLE) (mutex->backend)) == 0) 283 return -1; 284 else 285 return 0; 286} 287 288/* Backend condition mutex functions */ 289 290/* Allocate a condition. */ 291int 292__gthread_objc_condition_allocate (objc_condition_t condition) 293{ 294 /* Unimplemented. */ 295 return -1; 296} 297 298/* Deallocate a condition. */ 299int 300__gthread_objc_condition_deallocate (objc_condition_t condition) 301{ 302 /* Unimplemented. */ 303 return -1; 304} 305 306/* Wait on the condition */ 307int 308__gthread_objc_condition_wait (objc_condition_t condition, objc_mutex_t mutex) 309{ 310 /* Unimplemented. */ 311 return -1; 312} 313 314/* Wake up all threads waiting on this condition. */ 315int 316__gthread_objc_condition_broadcast (objc_condition_t condition) 317{ 318 /* Unimplemented. */ 319 return -1; 320} 321 322/* Wake up one thread waiting on this condition. */ 323int 324__gthread_objc_condition_signal (objc_condition_t condition) 325{ 326 /* Unimplemented. */ 327 return -1; 328} 329 330#else /* _LIBOBJC */ 331 332#ifdef __cplusplus 333extern "C" { 334#endif 335 336typedef unsigned long __gthread_key_t; 337 338typedef struct { 339 int done; 340 long started; 341} __gthread_once_t; 342 343typedef struct { 344 long counter; 345 void *sema; 346} __gthread_mutex_t; 347 348typedef struct { 349 long counter; 350 long depth; 351 unsigned long owner; 352 void *sema; 353} __gthread_recursive_mutex_t; 354 355#define __GTHREAD_ONCE_INIT {0, -1} 356#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function 357#define __GTHREAD_MUTEX_INIT_DEFAULT {-1, 0} 358#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION \ 359 __gthread_recursive_mutex_init_function 360#define __GTHREAD_RECURSIVE_MUTEX_INIT_DEFAULT {-1, 0, 0, 0} 361 362#if __MINGW32_MAJOR_VERSION >= 1 || \ 363 (__MINGW32_MAJOR_VERSION == 0 && __MINGW32_MINOR_VERSION > 2) 364#define MINGW32_SUPPORTS_MT_EH 1 365/* Mingw runtime >= v0.3 provides a magic variable that is set to nonzero 366 if -mthreads option was specified, or 0 otherwise. This is to get around 367 the lack of weak symbols in PE-COFF. */ 368extern int _CRT_MT; 369extern int __mingwthr_key_dtor (unsigned long, void (*) (void *)); 370#endif /* __MINGW32__ version */ 371 372/* The Windows95 kernel does not export InterlockedCompareExchange. 373 This provides a substitute. When building apps that reference 374 gthread_mutex_try_lock, the __GTHREAD_I486_INLINE_LOCK_PRIMITIVES 375 macro must be defined if Windows95 is a target. Currently 376 gthread_mutex_try_lock is not referenced by libgcc or libstdc++. */ 377#ifdef __GTHREAD_I486_INLINE_LOCK_PRIMITIVES 378static inline long 379__gthr_i486_lock_cmp_xchg(long *dest, long xchg, long comperand) 380{ 381 long result; 382 __asm__ __volatile__ ("\n\ 383 lock\n\ 384 cmpxchg{l} {%4, %1|%1, %4}\n" 385 : "=a" (result), "=m" (*dest) 386 : "0" (comperand), "m" (*dest), "r" (xchg) 387 : "cc"); 388 return result; 389} 390#define __GTHR_W32_InterlockedCompareExchange __gthr_i486_lock_cmp_xchg 391#else /* __GTHREAD_I486_INLINE_LOCK_PRIMITIVES */ 392#define __GTHR_W32_InterlockedCompareExchange InterlockedCompareExchange 393#endif /* __GTHREAD_I486_INLINE_LOCK_PRIMITIVES */ 394 395static inline int 396__gthread_active_p (void) 397{ 398#ifdef MINGW32_SUPPORTS_MT_EH 399 return _CRT_MT; 400#else 401 return 1; 402#endif 403} 404 405#if __GTHREAD_HIDE_WIN32API 406 407/* The implementations are in config/i386/gthr-win32.c in libgcc.a. 408 Only stubs are exposed to avoid polluting the C++ namespace with 409 windows api definitions. */ 410 411extern int __gthr_win32_once (__gthread_once_t *, void (*) (void)); 412extern int __gthr_win32_key_create (__gthread_key_t *, void (*) (void*)); 413extern int __gthr_win32_key_delete (__gthread_key_t); 414extern void * __gthr_win32_getspecific (__gthread_key_t); 415extern int __gthr_win32_setspecific (__gthread_key_t, const void *); 416extern void __gthr_win32_mutex_init_function (__gthread_mutex_t *); 417extern int __gthr_win32_mutex_lock (__gthread_mutex_t *); 418extern int __gthr_win32_mutex_trylock (__gthread_mutex_t *); 419extern int __gthr_win32_mutex_unlock (__gthread_mutex_t *); 420extern void 421 __gthr_win32_recursive_mutex_init_function (__gthread_recursive_mutex_t *); 422extern int __gthr_win32_recursive_mutex_lock (__gthread_recursive_mutex_t *); 423extern int 424 __gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *); 425extern int __gthr_win32_recursive_mutex_unlock (__gthread_recursive_mutex_t *); 426 427static inline int 428__gthread_once (__gthread_once_t *once, void (*func) (void)) 429{ 430 if (__gthread_active_p ()) 431 return __gthr_win32_once (once, func); 432 else 433 return -1; 434} 435 436static inline int 437__gthread_key_create (__gthread_key_t *key, void (*dtor) (void *)) 438{ 439 return __gthr_win32_key_create (key, dtor); 440} 441 442static inline int 443__gthread_key_delete (__gthread_key_t key) 444{ 445 return __gthr_win32_key_delete (key); 446} 447 448static inline void * 449__gthread_getspecific (__gthread_key_t key) 450{ 451 return __gthr_win32_getspecific (key); 452} 453 454static inline int 455__gthread_setspecific (__gthread_key_t key, const void *ptr) 456{ 457 return __gthr_win32_setspecific (key, ptr); 458} 459 460static inline void 461__gthread_mutex_init_function (__gthread_mutex_t *mutex) 462{ 463 __gthr_win32_mutex_init_function (mutex); 464} 465 466static inline int 467__gthread_mutex_lock (__gthread_mutex_t *mutex) 468{ 469 if (__gthread_active_p ()) 470 return __gthr_win32_mutex_lock (mutex); 471 else 472 return 0; 473} 474 475static inline int 476__gthread_mutex_trylock (__gthread_mutex_t *mutex) 477{ 478 if (__gthread_active_p ()) 479 return __gthr_win32_mutex_trylock (mutex); 480 else 481 return 0; 482} 483 484static inline int 485__gthread_mutex_unlock (__gthread_mutex_t *mutex) 486{ 487 if (__gthread_active_p ()) 488 return __gthr_win32_mutex_unlock (mutex); 489 else 490 return 0; 491} 492 493static inline void 494__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex) 495{ 496 __gthr_win32_recursive_mutex_init_function (mutex); 497} 498 499static inline int 500__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex) 501{ 502 if (__gthread_active_p ()) 503 return __gthr_win32_recursive_mutex_lock (mutex); 504 else 505 return 0; 506} 507 508static inline int 509__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex) 510{ 511 if (__gthread_active_p ()) 512 return __gthr_win32_recursive_mutex_trylock (mutex); 513 else 514 return 0; 515} 516 517static inline int 518__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex) 519{ 520 if (__gthread_active_p ()) 521 return __gthr_win32_recursive_mutex_unlock (mutex); 522 else 523 return 0; 524} 525 526#else /* ! __GTHREAD_HIDE_WIN32API */ 527 528#include <windows.h> 529#include <errno.h> 530 531static inline int 532__gthread_once (__gthread_once_t *once, void (*func) (void)) 533{ 534 if (! __gthread_active_p ()) 535 return -1; 536 else if (once == NULL || func == NULL) 537 return EINVAL; 538 539 if (! once->done) 540 { 541 if (InterlockedIncrement (&(once->started)) == 0) 542 { 543 (*func) (); 544 once->done = TRUE; 545 } 546 else 547 { 548 /* Another thread is currently executing the code, so wait for it 549 to finish; yield the CPU in the meantime. If performance 550 does become an issue, the solution is to use an Event that 551 we wait on here (and set above), but that implies a place to 552 create the event before this routine is called. */ 553 while (! once->done) 554 Sleep (0); 555 } 556 } 557 558 return 0; 559} 560 561/* Windows32 thread local keys don't support destructors; this leads to 562 leaks, especially in threaded applications making extensive use of 563 C++ EH. Mingw uses a thread-support DLL to work-around this problem. */ 564static inline int 565__gthread_key_create (__gthread_key_t *key, void (*dtor) (void *)) 566{ 567 int status = 0; 568 DWORD tls_index = TlsAlloc (); 569 if (tls_index != 0xFFFFFFFF) 570 { 571 *key = tls_index; 572#ifdef MINGW32_SUPPORTS_MT_EH 573 /* Mingw runtime will run the dtors in reverse order for each thread 574 when the thread exits. */ 575 status = __mingwthr_key_dtor (*key, dtor); 576#endif 577 } 578 else 579 status = (int) GetLastError (); 580 return status; 581} 582 583static inline int 584__gthread_key_delete (__gthread_key_t key) 585{ 586 return (TlsFree (key) != 0) ? 0 : (int) GetLastError (); 587} 588 589static inline void * 590__gthread_getspecific (__gthread_key_t key) 591{ 592 DWORD lasterror; 593 void *ptr; 594 595 lasterror = GetLastError (); 596 597 ptr = TlsGetValue (key); 598 599 SetLastError (lasterror); 600 601 return ptr; 602} 603 604static inline int 605__gthread_setspecific (__gthread_key_t key, const void *ptr) 606{ 607 return (TlsSetValue (key, (void*) ptr) != 0) ? 0 : (int) GetLastError (); 608} 609 610static inline void 611__gthread_mutex_init_function (__gthread_mutex_t *mutex) 612{ 613 mutex->counter = -1; 614 mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL); 615} 616 617static inline int 618__gthread_mutex_lock (__gthread_mutex_t *mutex) 619{ 620 int status = 0; 621 622 if (__gthread_active_p ()) 623 { 624 if (InterlockedIncrement (&mutex->counter) == 0 || 625 WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0) 626 status = 0; 627 else 628 { 629 /* WaitForSingleObject returns WAIT_FAILED, and we can only do 630 some best-effort cleanup here. */ 631 InterlockedDecrement (&mutex->counter); 632 status = 1; 633 } 634 } 635 return status; 636} 637 638static inline int 639__gthread_mutex_trylock (__gthread_mutex_t *mutex) 640{ 641 int status = 0; 642 643 if (__gthread_active_p ()) 644 { 645 if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0) 646 status = 0; 647 else 648 status = 1; 649 } 650 return status; 651} 652 653static inline int 654__gthread_mutex_unlock (__gthread_mutex_t *mutex) 655{ 656 if (__gthread_active_p ()) 657 { 658 if (InterlockedDecrement (&mutex->counter) >= 0) 659 return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1; 660 } 661 return 0; 662} 663 664static inline void 665__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex) 666{ 667 mutex->counter = -1; 668 mutex->depth = 0; 669 mutex->owner = 0; 670 mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL); 671} 672 673static inline int 674__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex) 675{ 676 if (__gthread_active_p ()) 677 { 678 DWORD me = GetCurrentThreadId(); 679 if (InterlockedIncrement (&mutex->counter) == 0) 680 { 681 mutex->depth = 1; 682 mutex->owner = me; 683 } 684 else if (mutex->owner == me) 685 { 686 InterlockedDecrement (&mutex->counter); 687 ++(mutex->depth); 688 } 689 else if (WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0) 690 { 691 mutex->depth = 1; 692 mutex->owner = me; 693 } 694 else 695 { 696 /* WaitForSingleObject returns WAIT_FAILED, and we can only do 697 some best-effort cleanup here. */ 698 InterlockedDecrement (&mutex->counter); 699 return 1; 700 } 701 } 702 return 0; 703} 704 705static inline int 706__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex) 707{ 708 if (__gthread_active_p ()) 709 { 710 DWORD me = GetCurrentThreadId(); 711 if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0) 712 { 713 mutex->depth = 1; 714 mutex->owner = me; 715 } 716 else if (mutex->owner == me) 717 ++(mutex->depth); 718 else 719 return 1; 720 } 721 return 0; 722} 723 724static inline int 725__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex) 726{ 727 if (__gthread_active_p ()) 728 { 729 --(mutex->depth); 730 if (mutex->depth == 0) 731 { 732 mutex->owner = 0; 733 734 if (InterlockedDecrement (&mutex->counter) >= 0) 735 return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1; 736 } 737 } 738 return 0; 739} 740 741#endif /* __GTHREAD_HIDE_WIN32API */ 742 743#ifdef __cplusplus 744} 745#endif 746 747#endif /* _LIBOBJC */ 748 749#endif /* ! GCC_GTHR_WIN32_H */ 750