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