2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / libgc / include / private / gc_locks.h
1 /* 
2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
4  * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
5  * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
6  *
7  *
8  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
9  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
10  *
11  * Permission is hereby granted to use or copy this program
12  * for any purpose,  provided the above notices are retained on all copies.
13  * Permission to modify the code and to distribute modified code is granted,
14  * provided the above notices are retained, and a notice that the code was
15  * modified is included with the above copyright notice.
16  */
17
18 #ifndef GC_LOCKS_H
19 #define GC_LOCKS_H
20
21 /*
22  * Mutual exclusion between allocator/collector routines.
23  * Needed if there is more than one allocator thread.
24  * FASTLOCK() is assumed to try to acquire the lock in a cheap and
25  * dirty way that is acceptable for a few instructions, e.g. by
26  * inhibiting preemption.  This is assumed to have succeeded only
27  * if a subsequent call to FASTLOCK_SUCCEEDED() returns TRUE.
28  * FASTUNLOCK() is called whether or not FASTLOCK_SUCCEEDED().
29  * If signals cannot be tolerated with the FASTLOCK held, then
30  * FASTLOCK should disable signals.  The code executed under
31  * FASTLOCK is otherwise immune to interruption, provided it is
32  * not restarted.
33  * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK
34  * and/or DISABLE_SIGNALS and ENABLE_SIGNALS and/or FASTLOCK.
35  * (There is currently no equivalent for FASTLOCK.)
36  *
37  * In the PARALLEL_MARK case, we also need to define a number of
38  * other inline finctions here:
39  *   GC_bool GC_compare_and_exchange( volatile GC_word *addr,
40  *                                    GC_word old, GC_word new )
41  *   GC_word GC_atomic_add( volatile GC_word *addr, GC_word how_much )
42  *   void GC_memory_barrier( )
43  *   
44  */  
45 # ifdef THREADS
46    void GC_noop1 GC_PROTO((word));
47 #  ifdef PCR_OBSOLETE   /* Faster, but broken with multiple lwp's       */
48 #    include  "th/PCR_Th.h"
49 #    include  "th/PCR_ThCrSec.h"
50      extern struct PCR_Th_MLRep GC_allocate_ml;
51 #    define DCL_LOCK_STATE  PCR_sigset_t GC_old_sig_mask
52 #    define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) 
53 #    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
54 #    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
55 #    define FASTLOCK() PCR_ThCrSec_EnterSys()
56      /* Here we cheat (a lot): */
57 #        define FASTLOCK_SUCCEEDED() (*(int *)(&GC_allocate_ml) == 0)
58                 /* TRUE if nobody currently holds the lock */
59 #    define FASTUNLOCK() PCR_ThCrSec_ExitSys()
60 #  endif
61 #  ifdef PCR
62 #    include <base/PCR_Base.h>
63 #    include <th/PCR_Th.h>
64      extern PCR_Th_ML GC_allocate_ml;
65 #    define DCL_LOCK_STATE \
66          PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask
67 #    define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
68 #    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
69 #    define FASTLOCK() (GC_fastLockRes = PCR_Th_ML_Try(&GC_allocate_ml))
70 #    define FASTLOCK_SUCCEEDED() (GC_fastLockRes == PCR_ERes_okay)
71 #    define FASTUNLOCK()  {\
72         if( FASTLOCK_SUCCEEDED() ) PCR_Th_ML_Release(&GC_allocate_ml); }
73 #  endif
74 #  ifdef SRC_M3
75      extern GC_word RT0u__inCritical;
76 #    define LOCK() RT0u__inCritical++
77 #    define UNLOCK() RT0u__inCritical--
78 #  endif
79 #  ifdef GC_SOLARIS_THREADS
80 #    include <thread.h>
81 #    include <signal.h>
82      extern mutex_t GC_allocate_ml;
83 #    define LOCK() mutex_lock(&GC_allocate_ml);
84 #    define UNLOCK() mutex_unlock(&GC_allocate_ml);
85 #  endif
86
87 /* Try to define GC_TEST_AND_SET and a matching GC_CLEAR for spin lock  */
88 /* acquisition and release.  We need this for correct operation of the  */
89 /* incremental GC.                                                      */
90 #  ifdef __GNUC__
91 #    if defined(I386)
92        inline static int GC_test_and_set(volatile unsigned int *addr) {
93           int oldval;
94           /* Note: the "xchg" instruction does not need a "lock" prefix */
95           __asm__ __volatile__("xchgl %0, %1"
96                 : "=r"(oldval), "=m"(*(addr))
97                 : "0"(1), "m"(*(addr)) : "memory");
98           return oldval;
99        }
100 #      define GC_TEST_AND_SET_DEFINED
101 #    endif
102 #    if defined(IA64)
103 #      if defined(__INTEL_COMPILER)
104 #        include <ia64intrin.h>
105 #      endif
106        inline static int GC_test_and_set(volatile unsigned int *addr) {
107           long oldval, n = 1;
108 #       ifndef __INTEL_COMPILER
109           __asm__ __volatile__("xchg4 %0=%1,%2"
110                 : "=r"(oldval), "=m"(*addr)
111                 : "r"(n), "1"(*addr) : "memory");
112 #       else
113           oldval = _InterlockedExchange(addr, n);
114 #       endif
115           return oldval;
116        }
117 #      define GC_TEST_AND_SET_DEFINED
118        /* Should this handle post-increment addressing?? */
119        inline static void GC_clear(volatile unsigned int *addr) {
120 #       ifndef __INTEL_COMPILER
121          __asm__ __volatile__("st4.rel %0=r0" : "=m" (*addr) : : "memory");
122 #       else
123         // there is no st4 but I can use xchg I hope
124          _InterlockedExchange(addr, 0);
125 #       endif
126        }
127 #      define GC_CLEAR_DEFINED
128 #    endif
129 #    ifdef SPARC
130        inline static int GC_test_and_set(volatile unsigned int *addr) {
131          int oldval;
132
133          __asm__ __volatile__("ldstub %1,%0"
134          : "=r"(oldval), "=m"(*addr)
135          : "m"(*addr) : "memory");
136          return oldval;
137        }
138 #      define GC_TEST_AND_SET_DEFINED
139 #    endif
140 #    ifdef M68K
141        /* Contributed by Tony Mantler.  I'm not sure how well it was    */
142        /* tested.                                                       */
143        inline static int GC_test_and_set(volatile unsigned int *addr) {
144           char oldval; /* this must be no longer than 8 bits */
145
146           /* The return value is semi-phony. */
147           /* 'tas' sets bit 7 while the return */
148           /* value pretends bit 0 was set */
149           __asm__ __volatile__(
150                  "tas %1@; sne %0; negb %0"
151                  : "=d" (oldval)
152                  : "a" (addr) : "memory");
153           return oldval;
154        }
155 #      define GC_TEST_AND_SET_DEFINED
156 #    endif
157 #    if defined(POWERPC)
158         inline static int GC_test_and_set(volatile unsigned int *addr) {
159           int oldval;
160           int temp = 1; /* locked value */
161
162           __asm__ __volatile__(
163                "1:\tlwarx %0,0,%3\n"   /* load and reserve               */
164                "\tcmpwi %0, 0\n"       /* if load is                     */
165                "\tbne 2f\n"            /*   non-zero, return already set */
166                "\tstwcx. %2,0,%1\n"    /* else store conditional         */
167                "\tbne- 1b\n"           /* retry if lost reservation      */
168                "\tsync\n"              /* import barrier                 */
169                "2:\t\n"                /* oldval is zero if we set       */
170               : "=&r"(oldval), "=p"(addr)
171               : "r"(temp), "1"(addr)
172               : "cr0","memory");
173           return oldval;
174         }
175 #       define GC_TEST_AND_SET_DEFINED
176         inline static void GC_clear(volatile unsigned int *addr) {
177           __asm__ __volatile__("eieio" : : : "memory");
178           *(addr) = 0;
179         }
180 #       define GC_CLEAR_DEFINED
181 #    endif
182 #    if defined(ALPHA) 
183         inline static int GC_test_and_set(volatile unsigned int * addr)
184         {
185           unsigned long oldvalue;
186           unsigned long temp;
187
188           __asm__ __volatile__(
189                              "1:     ldl_l %0,%1\n"
190                              "       and %0,%3,%2\n"
191                              "       bne %2,2f\n"
192                              "       xor %0,%3,%0\n"
193                              "       stl_c %0,%1\n"
194 #       ifdef __ELF__
195                              "       beq %0,3f\n"
196 #       else
197                              "       beq %0,1b\n"
198 #       endif
199                              "       mb\n"
200                              "2:\n"
201 #       ifdef __ELF__
202                              ".section .text2,\"ax\"\n"
203                              "3:     br 1b\n"
204                              ".previous"
205 #       endif
206                              :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue)
207                              :"Ir" (1), "m" (*addr)
208                              :"memory");
209
210           return oldvalue;
211         }
212 #       define GC_TEST_AND_SET_DEFINED
213         inline static void GC_clear(volatile unsigned int *addr) {
214           __asm__ __volatile__("mb" : : : "memory");
215           *(addr) = 0;
216         }
217 #       define GC_CLEAR_DEFINED
218 #    endif /* ALPHA */
219 #    ifdef ARM32
220         inline static int GC_test_and_set(volatile unsigned int *addr) {
221           int oldval;
222           /* SWP on ARM is very similar to XCHG on x86.  Doesn't lock the
223            * bus because there are no SMP ARM machines.  If/when there are,
224            * this code will likely need to be updated. */
225           /* See linuxthreads/sysdeps/arm/pt-machine.h in glibc-2.1 */
226           __asm__ __volatile__("swp %0, %1, [%2]"
227                              : "=r"(oldval)
228                              : "r"(1), "r"(addr)
229                              : "memory");
230           return oldval;
231         }
232 #       define GC_TEST_AND_SET_DEFINED
233 #    endif /* ARM32 */
234 #    ifdef S390
235        inline static int GC_test_and_set(volatile unsigned int *addr) {
236          int ret;
237          __asm__ __volatile__ (
238           "     l     %0,0(%2)\n"
239           "0:   cs    %0,%1,0(%2)\n"
240           "     jl    0b"
241           : "=&d" (ret)
242           : "d" (1), "a" (addr)
243           : "cc", "memory");
244          return ret;
245        }
246 #    endif
247 #  endif /* __GNUC__ */
248 #  if (defined(ALPHA) && !defined(__GNUC__))
249 #    ifndef OSF1
250         --> We currently assume that if gcc is not used, we are
251         --> running under Tru64.
252 #    endif
253 #    include <machine/builtins.h>
254 #    include <c_asm.h>
255 #    define GC_test_and_set(addr) __ATOMIC_EXCH_LONG(addr, 1)
256 #    define GC_TEST_AND_SET_DEFINED
257 #    define GC_clear(addr) { asm("mb"); *(volatile unsigned *)addr = 0; }
258 #    define GC_CLEAR_DEFINED
259 #  endif
260 #  if defined(MSWIN32)
261 #    define GC_test_and_set(addr) InterlockedExchange((LPLONG)addr,1)
262 #    define GC_TEST_AND_SET_DEFINED
263 #  endif
264 #  ifdef MIPS
265 #    ifdef LINUX
266 #      include <sys/tas.h>
267 #      define GC_test_and_set(addr) _test_and_set((int *) addr,1)
268 #      define GC_TEST_AND_SET_DEFINED
269 #    elif __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \
270         || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700
271 #        ifdef __GNUC__
272 #          define GC_test_and_set(addr) _test_and_set((void *)addr,1)
273 #        else
274 #          define GC_test_and_set(addr) test_and_set((void *)addr,1)
275 #        endif
276 #    else
277 #        define GC_test_and_set(addr) __test_and_set32((void *)addr,1)
278 #        define GC_clear(addr) __lock_release(addr);
279 #        define GC_CLEAR_DEFINED
280 #    endif
281 #    define GC_TEST_AND_SET_DEFINED
282 #  endif /* MIPS */
283 #  if defined(_AIX)
284 #    include <sys/atomic_op.h>
285 #    if (defined(_POWER) || defined(_POWERPC)) 
286 #      if defined(__GNUC__)  
287          inline static void GC_memsync() {
288            __asm__ __volatile__ ("sync" : : : "memory");
289          }
290 #      else
291 #        ifndef inline
292 #          define inline __inline
293 #        endif
294 #        pragma mc_func GC_memsync { \
295            "7c0004ac" /* sync (same opcode used for dcs)*/ \
296          }
297 #      endif
298 #    else 
299 #    error dont know how to memsync
300 #    endif
301      inline static int GC_test_and_set(volatile unsigned int * addr) {
302           int oldvalue = 0;
303           if (compare_and_swap((void *)addr, &oldvalue, 1)) {
304             GC_memsync();
305             return 0;
306           } else return 1;
307      }
308 #    define GC_TEST_AND_SET_DEFINED
309      inline static void GC_clear(volatile unsigned int *addr) {
310           GC_memsync();
311           *(addr) = 0;
312      }
313 #    define GC_CLEAR_DEFINED
314
315 #  endif
316 #  if 0 /* defined(HP_PA) */
317      /* The official recommendation seems to be to not use ldcw from    */
318      /* user mode.  Since multithreaded incremental collection doesn't  */
319      /* work anyway on HP_PA, this shouldn't be a major loss.           */
320
321      /* "set" means 0 and "clear" means 1 here.         */
322 #    define GC_test_and_set(addr) !GC_test_and_clear(addr);
323 #    define GC_TEST_AND_SET_DEFINED
324 #    define GC_clear(addr) GC_noop1((word)(addr)); *(volatile unsigned int *)addr = 1;
325         /* The above needs a memory barrier! */
326 #    define GC_CLEAR_DEFINED
327 #  endif
328 #  if defined(GC_TEST_AND_SET_DEFINED) && !defined(GC_CLEAR_DEFINED)
329 #    ifdef __GNUC__
330        inline static void GC_clear(volatile unsigned int *addr) {
331          /* Try to discourage gcc from moving anything past this. */
332          __asm__ __volatile__(" " : : : "memory");
333          *(addr) = 0;
334        }
335 #    else
336             /* The function call in the following should prevent the    */
337             /* compiler from moving assignments to below the UNLOCK.    */
338 #      define GC_clear(addr) GC_noop1((word)(addr)); \
339                              *((volatile unsigned int *)(addr)) = 0;
340 #    endif
341 #    define GC_CLEAR_DEFINED
342 #  endif /* !GC_CLEAR_DEFINED */
343
344 #  if !defined(GC_TEST_AND_SET_DEFINED)
345 #    define USE_PTHREAD_LOCKS
346 #  endif
347
348 #  if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
349       && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS)
350 #    define NO_THREAD (pthread_t)(-1)
351 #    include <pthread.h>
352 #    if defined(PARALLEL_MARK) 
353       /* We need compare-and-swap to update mark bits, where it's       */
354       /* performance critical.  If USE_MARK_BYTES is defined, it is     */
355       /* no longer needed for this purpose.  However we use it in       */
356       /* either case to implement atomic fetch-and-add, though that's   */
357       /* less performance critical, and could perhaps be done with      */
358       /* a lock.                                                        */
359 #     if defined(GENERIC_COMPARE_AND_SWAP)
360         /* Probably not useful, except for debugging.   */
361         /* We do use GENERIC_COMPARE_AND_SWAP on PA_RISC, but we        */
362         /* minimize its use.                                            */
363         extern pthread_mutex_t GC_compare_and_swap_lock;
364
365         /* Note that if GC_word updates are not atomic, a concurrent    */
366         /* reader should acquire GC_compare_and_swap_lock.  On          */
367         /* currently supported platforms, such updates are atomic.      */
368         extern GC_bool GC_compare_and_exchange(volatile GC_word *addr,
369                                                GC_word old, GC_word new_val);
370 #     endif /* GENERIC_COMPARE_AND_SWAP */
371 #     if defined(I386)
372 #      if !defined(GENERIC_COMPARE_AND_SWAP)
373          /* Returns TRUE if the comparison succeeded. */
374          inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
375                                                        GC_word old,
376                                                        GC_word new_val) 
377          {
378            char result;
379            __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1"
380                 : "+m"(*(addr)), "=r"(result)
381                 : "r" (new_val), "a"(old) : "memory");
382            return (GC_bool) result;
383          }
384 #      endif /* !GENERIC_COMPARE_AND_SWAP */
385        inline static void GC_memory_barrier()
386        {
387          /* We believe the processor ensures at least processor */
388          /* consistent ordering.  Thus a compiler barrier       */
389          /* should suffice.                                     */
390          __asm__ __volatile__("" : : : "memory");
391        }
392 #     endif /* I386 */
393
394 #     if defined(POWERPC)
395 #      if !defined(GENERIC_COMPARE_AND_SWAP)
396         /* Returns TRUE if the comparison succeeded. */
397         inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
398             GC_word old, GC_word new_val) 
399         {
400             int result, dummy;
401             __asm__ __volatile__(
402                 "1:\tlwarx %0,0,%5\n"
403                   "\tcmpw %0,%4\n"
404                   "\tbne  2f\n"
405                   "\tstwcx. %3,0,%2\n"
406                   "\tbne- 1b\n"
407                   "\tsync\n"
408                   "\tli %1, 1\n"
409                   "\tb 3f\n"
410                 "2:\tli %1, 0\n"
411                 "3:\t\n"
412                 :  "=&r" (dummy), "=r" (result), "=p" (addr)
413                 :  "r" (new_val), "r" (old), "2"(addr)
414                 : "cr0","memory");
415             return (GC_bool) result;
416         }
417 #      endif /* !GENERIC_COMPARE_AND_SWAP */
418         inline static void GC_memory_barrier()
419         {
420             __asm__ __volatile__("sync" : : : "memory");
421         }
422 #     endif /* POWERPC */
423
424 #     if defined(IA64)
425 #      if !defined(GENERIC_COMPARE_AND_SWAP)
426          inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
427                                                        GC_word old, GC_word new_val) 
428          {
429           unsigned long oldval;
430           __asm__ __volatile__("mov ar.ccv=%4 ;; cmpxchg8.rel %0=%1,%2,ar.ccv"
431                 : "=r"(oldval), "=m"(*addr)
432                 : "r"(new_val), "1"(*addr), "r"(old) : "memory");
433           return (oldval == old);
434          }
435 #      endif /* !GENERIC_COMPARE_AND_SWAP */
436 #      if 0
437         /* Shouldn't be needed; we use volatile stores instead. */
438         inline static void GC_memory_barrier()
439         {
440           __asm__ __volatile__("mf" : : : "memory");
441         }
442 #      endif /* 0 */
443 #     endif /* IA64 */
444 #     if defined(ALPHA)
445 #      if !defined(GENERIC_COMPARE_AND_SWAP)
446 #        if defined(__GNUC__)
447            inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
448                                                          GC_word old, GC_word new_val) 
449            {
450              unsigned long was_equal;
451              unsigned long temp;
452
453              __asm__ __volatile__(
454                              "1:     ldq_l %0,%1\n"
455                              "       cmpeq %0,%4,%2\n"
456                              "       mov %3,%0\n"
457                              "       beq %2,2f\n"
458                              "       stq_c %0,%1\n"
459                              "       beq %0,1b\n"
460                              "2:\n"
461                              "       mb\n"
462                              :"=&r" (temp), "=m" (*addr), "=&r" (was_equal)
463                              : "r" (new_val), "Ir" (old)
464                              :"memory");
465              return was_equal;
466            }
467 #        else /* !__GNUC__ */
468            inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
469                                                          GC_word old, GC_word new_val) 
470           {
471             return __CMP_STORE_QUAD(addr, old, new_val, addr);
472           }
473 #        endif /* !__GNUC__ */
474 #      endif /* !GENERIC_COMPARE_AND_SWAP */
475 #      ifdef __GNUC__
476          inline static void GC_memory_barrier()
477          {
478            __asm__ __volatile__("mb" : : : "memory");
479          }
480 #      else
481 #        define GC_memory_barrier() asm("mb")
482 #      endif /* !__GNUC__ */
483 #     endif /* ALPHA */
484 #     if defined(S390)
485 #      if !defined(GENERIC_COMPARE_AND_SWAP)
486          inline static GC_bool GC_compare_and_exchange(volatile C_word *addr,
487                                          GC_word old, GC_word new_val)
488          {
489            int retval;
490            __asm__ __volatile__ (
491 #            ifndef __s390x__
492                "     cs  %1,%2,0(%3)\n"
493 #            else
494                "     csg %1,%2,0(%3)\n"
495 #            endif
496              "     ipm %0\n"
497              "     srl %0,28\n"
498              : "=&d" (retval), "+d" (old)
499              : "d" (new_val), "a" (addr)
500              : "cc", "memory");
501            return retval == 0;
502          }
503 #      endif
504 #     endif
505 #     if !defined(GENERIC_COMPARE_AND_SWAP)
506         /* Returns the original value of *addr. */
507         inline static GC_word GC_atomic_add(volatile GC_word *addr,
508                                             GC_word how_much)
509         {
510           GC_word old;
511           do {
512             old = *addr;
513           } while (!GC_compare_and_exchange(addr, old, old+how_much));
514           return old;
515         }
516 #     else /* GENERIC_COMPARE_AND_SWAP */
517         /* So long as a GC_word can be atomically updated, it should    */
518         /* be OK to read *addr without a lock.                          */
519         extern GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much);
520 #     endif /* GENERIC_COMPARE_AND_SWAP */
521
522 #    endif /* PARALLEL_MARK */
523
524 #    if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS)
525       /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to   */
526       /* be held for long periods, if it is held at all.  Thus spinning */
527       /* and sleeping for fixed periods are likely to result in         */
528       /* significant wasted time.  We thus rely mostly on queued locks. */
529 #     define USE_SPIN_LOCK
530       extern volatile unsigned int GC_allocate_lock;
531       extern void GC_lock(void);
532         /* Allocation lock holder.  Only set if acquired by client through */
533         /* GC_call_with_alloc_lock.                                        */
534 #     ifdef GC_ASSERTIONS
535 #        define LOCK() \
536                 { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); \
537                   SET_LOCK_HOLDER(); }
538 #        define UNLOCK() \
539                 { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
540                   GC_clear(&GC_allocate_lock); }
541 #     else
542 #        define LOCK() \
543                 { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); }
544 #        define UNLOCK() \
545                 GC_clear(&GC_allocate_lock)
546 #     endif /* !GC_ASSERTIONS */
547 #     if 0
548         /* Another alternative for OSF1 might be:               */
549 #       include <sys/mman.h>
550         extern msemaphore GC_allocate_semaphore;
551 #       define LOCK() { if (msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) \
552                             != 0) GC_lock(); else GC_allocate_lock = 1; }
553         /* The following is INCORRECT, since the memory model is too weak. */
554         /* Is this true?  Presumably msem_unlock has the right semantics?  */
555         /*              - HB                                               */
556 #       define UNLOCK() { GC_allocate_lock = 0; \
557                           msem_unlock(&GC_allocate_semaphore, 0); }
558 #     endif /* 0 */
559 #    else /* THREAD_LOCAL_ALLOC  || USE_PTHREAD_LOCKS */
560 #      ifndef USE_PTHREAD_LOCKS
561 #        define USE_PTHREAD_LOCKS
562 #      endif
563 #    endif /* THREAD_LOCAL_ALLOC */
564 #   ifdef USE_PTHREAD_LOCKS
565 #      include <pthread.h>
566        extern pthread_mutex_t GC_allocate_ml;
567 #      ifdef GC_ASSERTIONS
568 #        define LOCK() \
569                 { GC_lock(); \
570                   SET_LOCK_HOLDER(); }
571 #        define UNLOCK() \
572                 { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
573                   pthread_mutex_unlock(&GC_allocate_ml); }
574 #      else /* !GC_ASSERTIONS */
575 #        if defined(NO_PTHREAD_TRYLOCK)
576 #          define LOCK() GC_lock();
577 #        else /* !defined(NO_PTHREAD_TRYLOCK) */
578 #        define LOCK() \
579            { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); }
580 #        endif
581 #        define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
582 #      endif /* !GC_ASSERTIONS */
583 #   endif /* USE_PTHREAD_LOCKS */
584 #   define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
585 #   define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
586 #   define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
587     extern VOLATILE GC_bool GC_collecting;
588 #   define ENTER_GC() GC_collecting = 1;
589 #   define EXIT_GC() GC_collecting = 0;
590     extern void GC_lock(void);
591     extern pthread_t GC_lock_holder;
592 #   ifdef GC_ASSERTIONS
593       extern pthread_t GC_mark_lock_holder;
594 #   endif
595 #  endif /* GC_PTHREADS with linux_threads.c implementation */
596 #  if defined(GC_IRIX_THREADS)
597 #    include <pthread.h>
598      /* This probably should never be included, but I can't test        */
599      /* on Irix anymore.                                                */
600 #    include <mutex.h>
601
602      extern volatile unsigned int GC_allocate_lock;
603         /* This is not a mutex because mutexes that obey the (optional)         */
604         /* POSIX scheduling rules are subject to convoys in high contention     */
605         /* applications.  This is basically a spin lock.                        */
606      extern pthread_t GC_lock_holder;
607      extern void GC_lock(void);
608         /* Allocation lock holder.  Only set if acquired by client through */
609         /* GC_call_with_alloc_lock.                                        */
610 #    define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
611 #    define NO_THREAD (pthread_t)(-1)
612 #    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
613 #    define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
614 #    define LOCK() { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); }
615 #    define UNLOCK() GC_clear(&GC_allocate_lock);
616      extern VOLATILE GC_bool GC_collecting;
617 #    define ENTER_GC() \
618                 { \
619                     GC_collecting = 1; \
620                 }
621 #    define EXIT_GC() GC_collecting = 0;
622 #  endif /* GC_IRIX_THREADS */
623 #  if defined(GC_WIN32_THREADS)
624 #    if defined(GC_PTHREADS)
625 #      include <pthread.h>
626        extern pthread_mutex_t GC_allocate_ml;
627 #      define LOCK()   pthread_mutex_lock(&GC_allocate_ml)
628 #      define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
629 #    else
630 #      include <windows.h>
631        GC_API CRITICAL_SECTION GC_allocate_ml;
632 #      define LOCK() EnterCriticalSection(&GC_allocate_ml);
633 #      define UNLOCK() LeaveCriticalSection(&GC_allocate_ml);
634 #    endif
635 #  endif
636 #  ifndef SET_LOCK_HOLDER
637 #      define SET_LOCK_HOLDER()
638 #      define UNSET_LOCK_HOLDER()
639 #      define I_HOLD_LOCK() FALSE
640                 /* Used on platforms were locks can be reacquired,      */
641                 /* so it doesn't matter if we lie.                      */
642 #  endif
643 # else /* !THREADS */
644 #    define LOCK()
645 #    define UNLOCK()
646 # endif /* !THREADS */
647 # ifndef SET_LOCK_HOLDER
648 #   define SET_LOCK_HOLDER()
649 #   define UNSET_LOCK_HOLDER()
650 #   define I_HOLD_LOCK() FALSE
651                 /* Used on platforms were locks can be reacquired,      */
652                 /* so it doesn't matter if we lie.                      */
653 # endif
654 # ifndef ENTER_GC
655 #   define ENTER_GC()
656 #   define EXIT_GC()
657 # endif
658
659 # ifndef DCL_LOCK_STATE
660 #   define DCL_LOCK_STATE
661 # endif
662 # ifndef FASTLOCK
663 #   define FASTLOCK() LOCK()
664 #   define FASTLOCK_SUCCEEDED() TRUE
665 #   define FASTUNLOCK() UNLOCK()
666 # endif
667
668 #endif /* GC_LOCKS_H */