1/* Threads compatibility routines for libgcc2 and libobjc. */ 2/* Compile this one with gcc. */ 3 4/* Copyright (C) 1999-2013 Free Software Foundation, Inc. 5 Contributed by Mumit Khan <khan@xraylith.wisc.edu>. 6 7This file is part of GCC. 8 9GCC is free software; you can redistribute it and/or modify it under 10the terms of the GNU General Public License as published by the Free 11Software Foundation; either version 3, or (at your option) any later 12version. 13 14GCC is distributed in the hope that it will be useful, but WITHOUT ANY 15WARRANTY; without even the implied warranty of MERCHANTABILITY or 16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17for more details. 18 19Under Section 7 of GPL version 3, you are granted additional 20permissions described in the GCC Runtime Library Exception, version 213.1, as published by the Free Software Foundation. 22 23You should have received a copy of the GNU General Public License and 24a copy of the GCC Runtime Library Exception along with this program; 25see 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 ___GLIBCXX_UNUSED_PARAM 75#define ___GLIBCXX_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 */ 90static DWORD __gthread_objc_data_tls = (DWORD) -1; 91 92/* Backend initialization functions */ 93 94/* Initialize the threads subsystem. */ 95int 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. */ 106int 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. */ 117objc_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. */ 131int 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. */ 158int 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. */ 187void 188__gthread_objc_thread_yield (void) 189{ 190 Sleep (0); 191} 192 193/* Terminate the current thread. */ 194int 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. */ 205objc_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. */ 212int 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. */ 222void * 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. */ 240int 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. */ 250int 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. */ 258int 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. */ 271int 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 */ 284int 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. */ 296int 297__gthread_objc_condition_allocate (objc_condition_t ___GLIBCXX_UNUSED_PARAM(condition)) 298{ 299 /* Unimplemented. */ 300 return -1; 301} 302 303/* Deallocate a condition. */ 304int 305__gthread_objc_condition_deallocate (objc_condition_t ___GLIBCXX_UNUSED_PARAM(condition)) 306{ 307 /* Unimplemented. */ 308 return -1; 309} 310 311/* Wait on the condition */ 312int 313__gthread_objc_condition_wait (objc_condition_t ___GLIBCXX_UNUSED_PARAM(condition), 314 objc_mutex_t ___GLIBCXX_UNUSED_PARAM(mutex)) 315{ 316 /* Unimplemented. */ 317 return -1; 318} 319 320/* Wake up all threads waiting on this condition. */ 321int 322__gthread_objc_condition_broadcast (objc_condition_t ___GLIBCXX_UNUSED_PARAM(condition)) 323{ 324 /* Unimplemented. */ 325 return -1; 326} 327 328/* Wake up one thread waiting on this condition. */ 329int 330__gthread_objc_condition_signal (objc_condition_t ___GLIBCXX_UNUSED_PARAM(condition)) 331{ 332 /* Unimplemented. */ 333 return -1; 334} 335 336#else /* _LIBOBJC */ 337 338#ifdef __cplusplus 339extern "C" { 340#endif 341 342typedef unsigned long __gthread_key_t; 343 344typedef struct { 345 int done; 346 long started; 347} __gthread_once_t; 348 349typedef struct { 350 long counter; 351 void *sema; 352} __gthread_mutex_t; 353 354typedef 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. */ 373extern int _CRT_MT; 374extern int __mingwthr_key_dtor (unsigned long, void (*) (void *)); 375#endif /* _WIN32 && !__CYGWIN__ */ 376 377/* The Windows95 kernel does not export InterlockedCompareExchange. 378 This provides a substitute. When building apps that reference 379 gthread_mutex_try_lock, the __GTHREAD_I486_INLINE_LOCK_PRIMITIVES 380 macro must be defined if Windows95 is a target. Currently 381 gthread_mutex_try_lock is not referenced by libgcc or libstdc++. */ 382#ifdef __GTHREAD_I486_INLINE_LOCK_PRIMITIVES 383static inline long 384__gthr_i486_lock_cmp_xchg(long *__dest, long __xchg, long __comperand) 385{ 386 long result; 387 __asm__ __volatile__ ("\n\ 388 lock\n\ 389 cmpxchg{l} {%4, %1|%1, %4}\n" 390 : "=a" (result), "=m" (*__dest) 391 : "0" (__comperand), "m" (*__dest), "r" (__xchg) 392 : "cc"); 393 return result; 394} 395#define __GTHR_W32_InterlockedCompareExchange __gthr_i486_lock_cmp_xchg 396#else /* __GTHREAD_I486_INLINE_LOCK_PRIMITIVES */ 397#define __GTHR_W32_InterlockedCompareExchange InterlockedCompareExchange 398#endif /* __GTHREAD_I486_INLINE_LOCK_PRIMITIVES */ 399 400static inline int 401__gthread_active_p (void) 402{ 403#ifdef MINGW32_SUPPORTS_MT_EH 404 return _CRT_MT; 405#else 406 return 1; 407#endif 408} 409 410#if __GTHREAD_HIDE_WIN32API 411 412/* The implementations are in config/i386/gthr-win32.c in libgcc.a. 413 Only stubs are exposed to avoid polluting the C++ namespace with 414 windows api definitions. */ 415 416extern int __gthr_win32_once (__gthread_once_t *, void (*) (void)); 417extern int __gthr_win32_key_create (__gthread_key_t *, void (*) (void*)); 418extern int __gthr_win32_key_delete (__gthread_key_t); 419extern void * __gthr_win32_getspecific (__gthread_key_t); 420extern int __gthr_win32_setspecific (__gthread_key_t, const void *); 421extern void __gthr_win32_mutex_init_function (__gthread_mutex_t *); 422extern int __gthr_win32_mutex_lock (__gthread_mutex_t *); 423extern int __gthr_win32_mutex_trylock (__gthread_mutex_t *); 424extern int __gthr_win32_mutex_unlock (__gthread_mutex_t *); 425extern void 426 __gthr_win32_recursive_mutex_init_function (__gthread_recursive_mutex_t *); 427extern int __gthr_win32_recursive_mutex_lock (__gthread_recursive_mutex_t *); 428extern int 429 __gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *); 430extern int __gthr_win32_recursive_mutex_unlock (__gthread_recursive_mutex_t *); 431extern void __gthr_win32_mutex_destroy (__gthread_mutex_t *); 432extern int 433 __gthr_win32_recursive_mutex_destroy (__gthread_recursive_mutex_t *); 434 435static inline int 436__gthread_once (__gthread_once_t *__once, void (*__func) (void)) 437{ 438 if (__gthread_active_p ()) 439 return __gthr_win32_once (__once, __func); 440 else 441 return -1; 442} 443 444static inline int 445__gthread_key_create (__gthread_key_t *__key, void (*__dtor) (void *)) 446{ 447 return __gthr_win32_key_create (__key, __dtor); 448} 449 450static inline int 451__gthread_key_delete (__gthread_key_t __key) 452{ 453 return __gthr_win32_key_delete (__key); 454} 455 456static inline void * 457__gthread_getspecific (__gthread_key_t __key) 458{ 459 return __gthr_win32_getspecific (__key); 460} 461 462static inline int 463__gthread_setspecific (__gthread_key_t __key, const void *__ptr) 464{ 465 return __gthr_win32_setspecific (__key, __ptr); 466} 467 468static inline void 469__gthread_mutex_init_function (__gthread_mutex_t *__mutex) 470{ 471 __gthr_win32_mutex_init_function (__mutex); 472} 473 474static inline void 475__gthread_mutex_destroy (__gthread_mutex_t *__mutex) 476{ 477 __gthr_win32_mutex_destroy (__mutex); 478} 479 480static inline int 481__gthread_mutex_lock (__gthread_mutex_t *__mutex) 482{ 483 if (__gthread_active_p ()) 484 return __gthr_win32_mutex_lock (__mutex); 485 else 486 return 0; 487} 488 489static inline int 490__gthread_mutex_trylock (__gthread_mutex_t *__mutex) 491{ 492 if (__gthread_active_p ()) 493 return __gthr_win32_mutex_trylock (__mutex); 494 else 495 return 0; 496} 497 498static inline int 499__gthread_mutex_unlock (__gthread_mutex_t *__mutex) 500{ 501 if (__gthread_active_p ()) 502 return __gthr_win32_mutex_unlock (__mutex); 503 else 504 return 0; 505} 506 507static inline void 508__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex) 509{ 510 __gthr_win32_recursive_mutex_init_function (__mutex); 511} 512 513static inline int 514__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex) 515{ 516 if (__gthread_active_p ()) 517 return __gthr_win32_recursive_mutex_lock (__mutex); 518 else 519 return 0; 520} 521 522static inline int 523__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex) 524{ 525 if (__gthread_active_p ()) 526 return __gthr_win32_recursive_mutex_trylock (__mutex); 527 else 528 return 0; 529} 530 531static inline int 532__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex) 533{ 534 if (__gthread_active_p ()) 535 return __gthr_win32_recursive_mutex_unlock (__mutex); 536 else 537 return 0; 538} 539 540static inline int 541__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex) 542{ 543 return __gthr_win32_recursive_mutex_destroy (__mutex); 544} 545 546#else /* ! __GTHREAD_HIDE_WIN32API */ 547 548#include <windows.h> 549#include <errno.h> 550 551static inline int 552__gthread_once (__gthread_once_t *__once, void (*__func) (void)) 553{ 554 if (! __gthread_active_p ()) 555 return -1; 556 else if (__once == NULL || __func == NULL) 557 return EINVAL; 558 559 if (! __once->done) 560 { 561 if (InterlockedIncrement (&(__once->started)) == 0) 562 { 563 (*__func) (); 564 __once->done = TRUE; 565 } 566 else 567 { 568 /* Another thread is currently executing the code, so wait for it 569 to finish; yield the CPU in the meantime. If performance 570 does become an issue, the solution is to use an Event that 571 we wait on here (and set above), but that implies a place to 572 create the event before this routine is called. */ 573 while (! __once->done) 574 Sleep (0); 575 } 576 } 577 578 return 0; 579} 580 581/* Windows32 thread local keys don't support destructors; this leads to 582 leaks, especially in threaded applications making extensive use of 583 C++ EH. Mingw uses a thread-support DLL to work-around this problem. */ 584static inline int 585__gthread_key_create (__gthread_key_t *__key, 586 void (*__dtor) (void *) __attribute__((unused))) 587{ 588 int __status = 0; 589 DWORD __tls_index = TlsAlloc (); 590 if (__tls_index != 0xFFFFFFFF) 591 { 592 *__key = __tls_index; 593#ifdef MINGW32_SUPPORTS_MT_EH 594 /* Mingw runtime will run the dtors in reverse order for each thread 595 when the thread exits. */ 596 __status = __mingwthr_key_dtor (*__key, __dtor); 597#endif 598 } 599 else 600 __status = (int) GetLastError (); 601 return __status; 602} 603 604static inline int 605__gthread_key_delete (__gthread_key_t __key) 606{ 607 return (TlsFree (__key) != 0) ? 0 : (int) GetLastError (); 608} 609 610static inline void * 611__gthread_getspecific (__gthread_key_t __key) 612{ 613 DWORD __lasterror; 614 void *__ptr; 615 616 __lasterror = GetLastError (); 617 618 __ptr = TlsGetValue (__key); 619 620 SetLastError (__lasterror); 621 622 return __ptr; 623} 624 625static inline int 626__gthread_setspecific (__gthread_key_t __key, const void *__ptr) 627{ 628 if (TlsSetValue (__key, CONST_CAST2(void *, const void *, __ptr)) != 0) 629 return 0; 630 else 631 return GetLastError (); 632} 633 634static inline void 635__gthread_mutex_init_function (__gthread_mutex_t *__mutex) 636{ 637 __mutex->counter = -1; 638 __mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL); 639} 640 641static inline void 642__gthread_mutex_destroy (__gthread_mutex_t *__mutex) 643{ 644 CloseHandle ((HANDLE) __mutex->sema); 645} 646 647static inline int 648__gthread_mutex_lock (__gthread_mutex_t *__mutex) 649{ 650 int __status = 0; 651 652 if (__gthread_active_p ()) 653 { 654 if (InterlockedIncrement (&__mutex->counter) == 0 || 655 WaitForSingleObject (__mutex->sema, INFINITE) == WAIT_OBJECT_0) 656 __status = 0; 657 else 658 { 659 /* WaitForSingleObject returns WAIT_FAILED, and we can only do 660 some best-effort cleanup here. */ 661 InterlockedDecrement (&__mutex->counter); 662 __status = 1; 663 } 664 } 665 return __status; 666} 667 668static inline int 669__gthread_mutex_trylock (__gthread_mutex_t *__mutex) 670{ 671 int __status = 0; 672 673 if (__gthread_active_p ()) 674 { 675 if (__GTHR_W32_InterlockedCompareExchange (&__mutex->counter, 0, -1) < 0) 676 __status = 0; 677 else 678 __status = 1; 679 } 680 return __status; 681} 682 683static inline int 684__gthread_mutex_unlock (__gthread_mutex_t *__mutex) 685{ 686 if (__gthread_active_p ()) 687 { 688 if (InterlockedDecrement (&__mutex->counter) >= 0) 689 return ReleaseSemaphore (__mutex->sema, 1, NULL) ? 0 : 1; 690 } 691 return 0; 692} 693 694static inline void 695__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex) 696{ 697 __mutex->counter = -1; 698 __mutex->depth = 0; 699 __mutex->owner = 0; 700 __mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL); 701} 702 703static inline int 704__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex) 705{ 706 if (__gthread_active_p ()) 707 { 708 DWORD __me = GetCurrentThreadId(); 709 if (InterlockedIncrement (&__mutex->counter) == 0) 710 { 711 __mutex->depth = 1; 712 __mutex->owner = __me; 713 } 714 else if (__mutex->owner == __me) 715 { 716 InterlockedDecrement (&__mutex->counter); 717 ++(__mutex->depth); 718 } 719 else if (WaitForSingleObject (__mutex->sema, INFINITE) == WAIT_OBJECT_0) 720 { 721 __mutex->depth = 1; 722 __mutex->owner = __me; 723 } 724 else 725 { 726 /* WaitForSingleObject returns WAIT_FAILED, and we can only do 727 some best-effort cleanup here. */ 728 InterlockedDecrement (&__mutex->counter); 729 return 1; 730 } 731 } 732 return 0; 733} 734 735static inline int 736__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex) 737{ 738 if (__gthread_active_p ()) 739 { 740 DWORD __me = GetCurrentThreadId(); 741 if (__GTHR_W32_InterlockedCompareExchange (&__mutex->counter, 0, -1) < 0) 742 { 743 __mutex->depth = 1; 744 __mutex->owner = __me; 745 } 746 else if (__mutex->owner == __me) 747 ++(__mutex->depth); 748 else 749 return 1; 750 } 751 return 0; 752} 753 754static inline int 755__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex) 756{ 757 if (__gthread_active_p ()) 758 { 759 --(__mutex->depth); 760 if (__mutex->depth == 0) 761 { 762 __mutex->owner = 0; 763 764 if (InterlockedDecrement (&__mutex->counter) >= 0) 765 return ReleaseSemaphore (__mutex->sema, 1, NULL) ? 0 : 1; 766 } 767 } 768 return 0; 769} 770 771static inline int 772__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex) 773{ 774 CloseHandle ((HANDLE) __mutex->sema); 775 return 0; 776} 777 778#endif /* __GTHREAD_HIDE_WIN32API */ 779 780#ifdef __cplusplus 781} 782#endif 783 784#endif /* _LIBOBJC */ 785 786#endif /* ! GCC_GTHR_WIN32_H */ 787