i386.c (enum pta_flags): Move out of struct scope...
[gcc.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
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 2, 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 You should have received a copy of the GNU General Public License
21 along with GCC; see the file COPYING. If not, write to the Free
22 Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-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 */
85 static DWORD __gthread_objc_data_tls = (DWORD) -1;
86
87 /* Backend initialization functions */
88
89 /* Initialize the threads subsystem. */
90 int
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. */
101 int
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. */
112 objc_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. */
126 int
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. */
153 int
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. */
182 void
183 __gthread_objc_thread_yield (void)
184 {
185 Sleep (0);
186 }
187
188 /* Terminate the current thread. */
189 int
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. */
200 objc_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. */
207 int
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. */
217 void *
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. */
235 int
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. */
245 int
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. */
253 int
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. */
266 int
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 */
279 int
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. */
291 int
292 __gthread_objc_condition_allocate (objc_condition_t condition)
293 {
294 /* Unimplemented. */
295 return -1;
296 }
297
298 /* Deallocate a condition. */
299 int
300 __gthread_objc_condition_deallocate (objc_condition_t condition)
301 {
302 /* Unimplemented. */
303 return -1;
304 }
305
306 /* Wait on the condition */
307 int
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. */
315 int
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. */
323 int
324 __gthread_objc_condition_signal (objc_condition_t condition)
325 {
326 /* Unimplemented. */
327 return -1;
328 }
329
330 #else /* _LIBOBJC */
331
332 #ifdef __cplusplus
333 extern "C" {
334 #endif
335
336 typedef unsigned long __gthread_key_t;
337
338 typedef struct {
339 int done;
340 long started;
341 } __gthread_once_t;
342
343 typedef struct {
344 long counter;
345 void *sema;
346 } __gthread_mutex_t;
347
348 typedef 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. */
368 extern int _CRT_MT;
369 extern 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
378 static 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
395 static 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
411 extern int __gthr_win32_once (__gthread_once_t *, void (*) (void));
412 extern int __gthr_win32_key_create (__gthread_key_t *, void (*) (void*));
413 extern int __gthr_win32_key_delete (__gthread_key_t);
414 extern void * __gthr_win32_getspecific (__gthread_key_t);
415 extern int __gthr_win32_setspecific (__gthread_key_t, const void *);
416 extern void __gthr_win32_mutex_init_function (__gthread_mutex_t *);
417 extern int __gthr_win32_mutex_lock (__gthread_mutex_t *);
418 extern int __gthr_win32_mutex_trylock (__gthread_mutex_t *);
419 extern int __gthr_win32_mutex_unlock (__gthread_mutex_t *);
420 extern void
421 __gthr_win32_recursive_mutex_init_function (__gthread_recursive_mutex_t *);
422 extern int __gthr_win32_recursive_mutex_lock (__gthread_recursive_mutex_t *);
423 extern int
424 __gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *);
425 extern int __gthr_win32_recursive_mutex_unlock (__gthread_recursive_mutex_t *);
426
427 static 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
436 static inline int
437 __gthread_key_create (__gthread_key_t *key, void (*dtor) (void *))
438 {
439 return __gthr_win32_key_create (key, dtor);
440 }
441
442 static inline int
443 __gthread_key_delete (__gthread_key_t key)
444 {
445 return __gthr_win32_key_delete (key);
446 }
447
448 static inline void *
449 __gthread_getspecific (__gthread_key_t key)
450 {
451 return __gthr_win32_getspecific (key);
452 }
453
454 static inline int
455 __gthread_setspecific (__gthread_key_t key, const void *ptr)
456 {
457 return __gthr_win32_setspecific (key, ptr);
458 }
459
460 static inline void
461 __gthread_mutex_init_function (__gthread_mutex_t *mutex)
462 {
463 __gthr_win32_mutex_init_function (mutex);
464 }
465
466 static 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
475 static 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
484 static 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
493 static inline void
494 __gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
495 {
496 __gthr_win32_recursive_mutex_init_function (mutex);
497 }
498
499 static 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
508 static 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
517 static 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
531 static 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. */
564 static inline int
565 __gthread_key_create (__gthread_key_t *key,
566 void (*dtor) (void *) __attribute__((unused)))
567 {
568 int status = 0;
569 DWORD tls_index = TlsAlloc ();
570 if (tls_index != 0xFFFFFFFF)
571 {
572 *key = tls_index;
573 #ifdef MINGW32_SUPPORTS_MT_EH
574 /* Mingw runtime will run the dtors in reverse order for each thread
575 when the thread exits. */
576 status = __mingwthr_key_dtor (*key, dtor);
577 #endif
578 }
579 else
580 status = (int) GetLastError ();
581 return status;
582 }
583
584 static inline int
585 __gthread_key_delete (__gthread_key_t key)
586 {
587 return (TlsFree (key) != 0) ? 0 : (int) GetLastError ();
588 }
589
590 static inline void *
591 __gthread_getspecific (__gthread_key_t key)
592 {
593 DWORD lasterror;
594 void *ptr;
595
596 lasterror = GetLastError ();
597
598 ptr = TlsGetValue (key);
599
600 SetLastError (lasterror);
601
602 return ptr;
603 }
604
605 static inline int
606 __gthread_setspecific (__gthread_key_t key, const void *ptr)
607 {
608 return (TlsSetValue (key, (void*) ptr) != 0) ? 0 : (int) GetLastError ();
609 }
610
611 static inline void
612 __gthread_mutex_init_function (__gthread_mutex_t *mutex)
613 {
614 mutex->counter = -1;
615 mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL);
616 }
617
618 static inline int
619 __gthread_mutex_lock (__gthread_mutex_t *mutex)
620 {
621 int status = 0;
622
623 if (__gthread_active_p ())
624 {
625 if (InterlockedIncrement (&mutex->counter) == 0 ||
626 WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0)
627 status = 0;
628 else
629 {
630 /* WaitForSingleObject returns WAIT_FAILED, and we can only do
631 some best-effort cleanup here. */
632 InterlockedDecrement (&mutex->counter);
633 status = 1;
634 }
635 }
636 return status;
637 }
638
639 static inline int
640 __gthread_mutex_trylock (__gthread_mutex_t *mutex)
641 {
642 int status = 0;
643
644 if (__gthread_active_p ())
645 {
646 if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0)
647 status = 0;
648 else
649 status = 1;
650 }
651 return status;
652 }
653
654 static inline int
655 __gthread_mutex_unlock (__gthread_mutex_t *mutex)
656 {
657 if (__gthread_active_p ())
658 {
659 if (InterlockedDecrement (&mutex->counter) >= 0)
660 return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1;
661 }
662 return 0;
663 }
664
665 static inline void
666 __gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
667 {
668 mutex->counter = -1;
669 mutex->depth = 0;
670 mutex->owner = 0;
671 mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL);
672 }
673
674 static inline int
675 __gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
676 {
677 if (__gthread_active_p ())
678 {
679 DWORD me = GetCurrentThreadId();
680 if (InterlockedIncrement (&mutex->counter) == 0)
681 {
682 mutex->depth = 1;
683 mutex->owner = me;
684 }
685 else if (mutex->owner == me)
686 {
687 InterlockedDecrement (&mutex->counter);
688 ++(mutex->depth);
689 }
690 else if (WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0)
691 {
692 mutex->depth = 1;
693 mutex->owner = me;
694 }
695 else
696 {
697 /* WaitForSingleObject returns WAIT_FAILED, and we can only do
698 some best-effort cleanup here. */
699 InterlockedDecrement (&mutex->counter);
700 return 1;
701 }
702 }
703 return 0;
704 }
705
706 static inline int
707 __gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
708 {
709 if (__gthread_active_p ())
710 {
711 DWORD me = GetCurrentThreadId();
712 if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0)
713 {
714 mutex->depth = 1;
715 mutex->owner = me;
716 }
717 else if (mutex->owner == me)
718 ++(mutex->depth);
719 else
720 return 1;
721 }
722 return 0;
723 }
724
725 static inline int
726 __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
727 {
728 if (__gthread_active_p ())
729 {
730 --(mutex->depth);
731 if (mutex->depth == 0)
732 {
733 mutex->owner = 0;
734
735 if (InterlockedDecrement (&mutex->counter) >= 0)
736 return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1;
737 }
738 }
739 return 0;
740 }
741
742 #endif /* __GTHREAD_HIDE_WIN32API */
743
744 #ifdef __cplusplus
745 }
746 #endif
747
748 #endif /* _LIBOBJC */
749
750 #endif /* ! GCC_GTHR_WIN32_H */