OSDN Git Service

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