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