[io-layer] add URLs for some ximian bug numbers in sockets.cs
[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 SN_TARGET_PS3
80 #    include <pthread.h>
81      extern pthread_mutex_t GC_allocate_ml;
82 #      define LOCK()   pthread_mutex_lock(&GC_allocate_ml)
83 #      define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
84 #  endif
85 #  ifdef GC_SOLARIS_THREADS
86 #    include <thread.h>
87 #    include <signal.h>
88      extern mutex_t GC_allocate_ml;
89 #    define LOCK() mutex_lock(&GC_allocate_ml);
90 #    define UNLOCK() mutex_unlock(&GC_allocate_ml);
91 #  endif
92
93 /* Try to define GC_TEST_AND_SET and a matching GC_CLEAR for spin lock  */
94 /* acquisition and release.  We need this for correct operation of the  */
95 /* incremental GC.                                                      */
96 #  ifdef __GNUC__
97 #    if defined(I386)
98        inline static int GC_test_and_set(volatile unsigned int *addr) {
99           int oldval;
100           /* Note: the "xchg" instruction does not need a "lock" prefix */
101           __asm__ __volatile__("xchgl %0, %1"
102                 : "=r"(oldval), "=m"(*(addr))
103                 : "0"(1), "m"(*(addr)) : "memory");
104           return oldval;
105        }
106 #      define GC_TEST_AND_SET_DEFINED
107 #    endif
108 #    if defined(IA64)
109 #      if defined(__INTEL_COMPILER)
110 #        include <ia64intrin.h>
111 #      endif
112        inline static int GC_test_and_set(volatile unsigned int *addr) {
113           long oldval, n = 1;
114 #       ifndef __INTEL_COMPILER
115           __asm__ __volatile__("xchg4 %0=%1,%2"
116                 : "=r"(oldval), "=m"(*addr)
117                 : "r"(n) : "memory");
118 #       else
119           oldval = _InterlockedExchange(addr, n);
120 #       endif
121           return oldval;
122        }
123 #      define GC_TEST_AND_SET_DEFINED
124        /* Should this handle post-increment addressing?? */
125        inline static void GC_clear(volatile unsigned int *addr) {
126 #       ifndef __INTEL_COMPILER
127          __asm__ __volatile__("st4.rel %0=r0" : "=m" (*addr) : : "memory");
128 #       else
129         // there is no st4 but I can use xchg I hope
130          _InterlockedExchange(addr, 0);
131 #       endif
132        }
133 #      define GC_CLEAR_DEFINED
134 #    endif
135 #    ifdef SPARC
136        inline static int GC_test_and_set(volatile unsigned int *addr) {
137          int oldval;
138
139          __asm__ __volatile__("ldstub %1,%0"
140          : "=r"(oldval), "=m"(*addr)
141          : "m"(*addr) : "memory");
142          return oldval;
143        }
144 #      define GC_TEST_AND_SET_DEFINED
145 #    endif
146 #    ifdef M68K
147        /* Contributed by Tony Mantler.  I'm not sure how well it was    */
148        /* tested.                                                       */
149        inline static int GC_test_and_set(volatile unsigned int *addr) {
150           char oldval; /* this must be no longer than 8 bits */
151
152           /* The return value is semi-phony. */
153           /* 'tas' sets bit 7 while the return */
154           /* value pretends bit 0 was set */
155           __asm__ __volatile__(
156                  "tas %1@; sne %0; negb %0"
157                  : "=d" (oldval)
158                  : "a" (addr) : "memory");
159           return oldval;
160        }
161 #      define GC_TEST_AND_SET_DEFINED
162 #    endif
163 #    if defined(POWERPC)
164         inline static int GC_test_and_set(volatile unsigned int *addr) {
165           int oldval;
166           int temp = 1; /* locked value */
167
168           __asm__ __volatile__(
169                "1:\tlwarx %0,0,%1\n"   /* load and reserve               */
170                "\tcmpwi %0, 0\n"       /* if load is                     */
171                "\tbne 2f\n"            /*   non-zero, return already set */
172                "\tstwcx. %2,0,%1\n"    /* else store conditional         */
173                "\tbne- 1b\n"           /* retry if lost reservation      */
174                "\tsync\n"              /* import barrier                 */
175                "2:\t\n"                /* oldval is zero if we set       */
176               : "=&r"(oldval)
177               : "r"(addr), "r"(temp)
178               : "cr0","memory");
179           return oldval;
180         }
181 #     define GC_TEST_AND_SET_DEFINED
182       inline static void GC_clear(volatile unsigned int *addr) {
183         __asm__ __volatile__("lwsync" : : : "memory");
184         *(addr) = 0;
185       }
186 #     define GC_CLEAR_DEFINED
187 #    endif
188 #    if defined(ALPHA) 
189         inline static int GC_test_and_set(volatile unsigned int * addr)
190         {
191           unsigned long oldvalue;
192           unsigned long temp;
193
194           __asm__ __volatile__(
195                              "1:     ldl_l %0,%1\n"
196                              "       and %0,%3,%2\n"
197                              "       bne %2,2f\n"
198                              "       xor %0,%3,%0\n"
199                              "       stl_c %0,%1\n"
200 #       ifdef __ELF__
201                              "       beq %0,3f\n"
202 #       else
203                              "       beq %0,1b\n"
204 #       endif
205                              "       mb\n"
206                              "2:\n"
207 #       ifdef __ELF__
208                              ".section .text2,\"ax\"\n"
209                              "3:     br 1b\n"
210                              ".previous"
211 #       endif
212                              :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue)
213                              :"Ir" (1), "m" (*addr)
214                              :"memory");
215
216           return oldvalue;
217         }
218 #       define GC_TEST_AND_SET_DEFINED
219         inline static void GC_clear(volatile unsigned int *addr) {
220           __asm__ __volatile__("mb" : : : "memory");
221           *(addr) = 0;
222         }
223 #       define GC_CLEAR_DEFINED
224 #    endif /* ALPHA */
225 #    ifdef ARM32
226         inline static int GC_test_and_set(volatile unsigned int *addr) {
227 #if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7__)
228           int ret, tmp;
229           __asm__ __volatile__ (
230                                  "1:\n"
231                                  "ldrex %0, [%3]\n"
232                                  "strex %1, %2, [%3]\n" 
233                                  "teq %1, #0\n"
234                                  "bne 1b\n"
235                                  : "=&r" (ret), "=&r" (tmp)
236                                  : "r" (1), "r" (addr)
237                                  : "memory", "cc");
238           return ret;
239 #else
240           int oldval;
241           /* SWP on ARM is very similar to XCHG on x86.  Doesn't lock the
242            * bus because there are no SMP ARM machines.  If/when there are,
243            * this code will likely need to be updated. */
244           /* See linuxthreads/sysdeps/arm/pt-machine.h in glibc-2.1 */
245           __asm__ __volatile__("swp %0, %1, [%2]"
246                              : "=&r"(oldval)
247                              : "r"(1), "r"(addr)
248                              : "memory");
249           return oldval;
250 #endif
251         }
252 #       define GC_TEST_AND_SET_DEFINED
253       inline static void GC_clear(volatile unsigned int *addr) {
254 #ifdef HAVE_ARMV6
255                   /* Memory barrier */
256                   __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory");
257 #endif
258                   *(addr) = 0;
259       }
260 #     define GC_CLEAR_DEFINED
261 #    endif /* ARM32 */
262 #    ifdef CRIS
263         inline static int GC_test_and_set(volatile unsigned int *addr) {
264           /* Ripped from linuxthreads/sysdeps/cris/pt-machine.h.        */
265           /* Included with Hans-Peter Nilsson's permission.             */
266           register unsigned long int ret;
267
268           /* Note the use of a dummy output of *addr to expose the write.
269            * The memory barrier is to stop *other* writes being moved past
270            * this code.
271            */
272             __asm__ __volatile__("clearf\n"
273                                  "0:\n\t"
274                                  "movu.b [%2],%0\n\t"
275                                  "ax\n\t"
276                                  "move.b %3,[%2]\n\t"
277                                  "bwf 0b\n\t"
278                                  "clearf"
279                                  : "=&r" (ret), "=m" (*addr)
280                                  : "r" (addr), "r" ((int) 1), "m" (*addr)
281                                  : "memory");
282             return ret;
283         }
284 #       define GC_TEST_AND_SET_DEFINED
285 #    endif /* CRIS */
286 #    ifdef S390
287        inline static int GC_test_and_set(volatile unsigned int *addr) {
288          int ret;
289          __asm__ __volatile__ (
290           "     l     %0,0(%2)\n"
291           "0:   cs    %0,%1,0(%2)\n"
292           "     jl    0b"
293           : "=&d" (ret)
294           : "d" (1), "a" (addr)
295           : "cc", "memory");
296          return ret;
297        }
298 #    endif
299 #  endif /* __GNUC__ */
300 #  if (defined(ALPHA) && !defined(__GNUC__))
301 #    ifndef OSF1
302         --> We currently assume that if gcc is not used, we are
303         --> running under Tru64.
304 #    endif
305 #    include <machine/builtins.h>
306 #    include <c_asm.h>
307 #    define GC_test_and_set(addr) __ATOMIC_EXCH_LONG(addr, 1)
308 #    define GC_TEST_AND_SET_DEFINED
309 #    define GC_clear(addr) { asm("mb"); *(volatile unsigned *)addr = 0; }
310 #    define GC_CLEAR_DEFINED
311 #  endif
312 #  if defined(MSWIN32)
313 #    define GC_test_and_set(addr) InterlockedExchange((LPLONG)addr,1)
314 #    define GC_TEST_AND_SET_DEFINED
315 #  endif
316 #  ifdef MIPS
317 #    ifdef LINUX
318 #      include <sys/tas.h>
319 #      define GC_test_and_set(addr) _test_and_set((int *) addr,1)
320 #      define GC_TEST_AND_SET_DEFINED
321 #    elif __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \
322         || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700
323 #        ifdef __GNUC__
324 #          define GC_test_and_set(addr) _test_and_set((void *)addr,1)
325 #        else
326 #          define GC_test_and_set(addr) test_and_set((void *)addr,1)
327 #        endif
328 #    else
329 #        include <sgidefs.h>
330 #        include <mutex.h>
331 #        define GC_test_and_set(addr) __test_and_set32((void *)addr,1)
332 #        define GC_clear(addr) __lock_release(addr);
333 #        define GC_CLEAR_DEFINED
334 #    endif
335 #    define GC_TEST_AND_SET_DEFINED
336 #  endif /* MIPS */
337 #  if defined(_AIX)
338 #    include <sys/atomic_op.h>
339 #    if (defined(_POWER) || defined(_POWERPC)) 
340 #      if defined(__GNUC__)  
341          inline static void GC_memsync() {
342            __asm__ __volatile__ ("sync" : : : "memory");
343          }
344 #      else
345 #        ifndef inline
346 #          define inline __inline
347 #        endif
348 #        pragma mc_func GC_memsync { \
349            "7c0004ac" /* sync (same opcode used for dcs)*/ \
350          }
351 #      endif
352 #    else 
353 #    error dont know how to memsync
354 #    endif
355      inline static int GC_test_and_set(volatile unsigned int * addr) {
356           int oldvalue = 0;
357           if (compare_and_swap((void *)addr, &oldvalue, 1)) {
358             GC_memsync();
359             return 0;
360           } else return 1;
361      }
362 #    define GC_TEST_AND_SET_DEFINED
363      inline static void GC_clear(volatile unsigned int *addr) {
364           GC_memsync();
365           *(addr) = 0;
366      }
367 #    define GC_CLEAR_DEFINED
368
369 #  endif
370 #  if 0 /* defined(HP_PA) */
371      /* The official recommendation seems to be to not use ldcw from    */
372      /* user mode.  Since multithreaded incremental collection doesn't  */
373      /* work anyway on HP_PA, this shouldn't be a major loss.           */
374
375      /* "set" means 0 and "clear" means 1 here.         */
376 #    define GC_test_and_set(addr) !GC_test_and_clear(addr);
377 #    define GC_TEST_AND_SET_DEFINED
378 #    define GC_clear(addr) GC_noop1((word)(addr)); *(volatile unsigned int *)addr = 1;
379         /* The above needs a memory barrier! */
380 #    define GC_CLEAR_DEFINED
381 #  endif
382 #  if defined(GC_TEST_AND_SET_DEFINED) && !defined(GC_CLEAR_DEFINED)
383 #    ifdef __GNUC__
384        inline static void GC_clear(volatile unsigned int *addr) {
385          /* Try to discourage gcc from moving anything past this. */
386          __asm__ __volatile__(" " : : : "memory");
387          *(addr) = 0;
388        }
389 #    else
390             /* The function call in the following should prevent the    */
391             /* compiler from moving assignments to below the UNLOCK.    */
392 #      define GC_clear(addr) GC_noop1((word)(addr)); \
393                              *((volatile unsigned int *)(addr)) = 0;
394 #    endif
395 #    define GC_CLEAR_DEFINED
396 #  endif /* !GC_CLEAR_DEFINED */
397
398 #  if !defined(GC_TEST_AND_SET_DEFINED)
399 #    define USE_PTHREAD_LOCKS
400 #  endif
401
402 #  if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
403       && !defined(GC_WIN32_THREADS)
404 #    define NO_THREAD (pthread_t)(-1)
405 #    include <pthread.h>
406 #    if defined(PARALLEL_MARK) 
407       /* We need compare-and-swap to update mark bits, where it's       */
408       /* performance critical.  If USE_MARK_BYTES is defined, it is     */
409       /* no longer needed for this purpose.  However we use it in       */
410       /* either case to implement atomic fetch-and-add, though that's   */
411       /* less performance critical, and could perhaps be done with      */
412       /* a lock.                                                        */
413 #     if defined(GENERIC_COMPARE_AND_SWAP)
414         /* Probably not useful, except for debugging.   */
415         /* We do use GENERIC_COMPARE_AND_SWAP on PA_RISC, but we        */
416         /* minimize its use.                                            */
417         extern pthread_mutex_t GC_compare_and_swap_lock;
418
419         /* Note that if GC_word updates are not atomic, a concurrent    */
420         /* reader should acquire GC_compare_and_swap_lock.  On          */
421         /* currently supported platforms, such updates are atomic.      */
422         extern GC_bool GC_compare_and_exchange(volatile GC_word *addr,
423                                                GC_word old, GC_word new_val);
424 #     endif /* GENERIC_COMPARE_AND_SWAP */
425 #     if defined(I386)
426 #      if !defined(GENERIC_COMPARE_AND_SWAP)
427          /* Returns TRUE if the comparison succeeded. */
428          inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
429                                                        GC_word old,
430                                                        GC_word new_val) 
431          {
432            char result;
433            __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1"
434                 : "+m"(*(addr)), "=q"(result)
435                 : "r" (new_val), "a"(old) : "memory");
436            return (GC_bool) result;
437          }
438 #      endif /* !GENERIC_COMPARE_AND_SWAP */
439        inline static void GC_memory_barrier()
440        {
441          /* We believe the processor ensures at least processor */
442          /* consistent ordering.  Thus a compiler barrier       */
443          /* should suffice.                                     */
444          __asm__ __volatile__("" : : : "memory");
445        }
446 #     endif /* I386 */
447
448 #     if defined(X86_64)
449 #      if !defined(GENERIC_COMPARE_AND_SWAP)
450          /* Returns TRUE if the comparison succeeded. */
451          inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
452                                                        GC_word old,
453                                                        GC_word new_val) 
454          {
455            char result;
456            __asm__ __volatile__("lock; cmpxchgq %2, %0; setz %1"
457                 : "+m"(*(addr)), "=r"(result)
458                 : "r" (new_val), "a"(old) : "memory");
459            return (GC_bool) result;
460          }
461 #      endif /* !GENERIC_COMPARE_AND_SWAP */
462        inline static void GC_memory_barrier()
463        {
464          /* We believe the processor ensures at least processor */
465          /* consistent ordering.  Thus a compiler barrier       */
466          /* should suffice.                                     */
467          __asm__ __volatile__("" : : : "memory");
468        }
469 #     endif /* X86_64 */
470
471 #     if defined(POWERPC)
472 #      if !defined(GENERIC_COMPARE_AND_SWAP)
473 #       if CPP_WORDSZ == 64
474         /* Returns TRUE if the comparison succeeded. */
475         inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
476             GC_word old, GC_word new_val) 
477         {
478 #         if HAS___SYNC_BOOL_COMPARE_AND_SWAP
479             return __sync_bool_compare_and_swap(addr, old, new_val);
480 #         else
481             unsigned long result, dummy;
482             __asm__ __volatile__(
483                 "1:\tldarx %0,0,%5\n"
484                   "\tcmpd %0,%4\n"
485                   "\tbne  2f\n"
486                   "\tstdcx. %3,0,%2\n"
487                   "\tbne- 1b\n"
488                   "\tsync\n"
489                   "\tli %1, 1\n"
490                   "\tb 3f\n"
491                 "2:\tli %1, 0\n"
492                 "3:\t\n"
493                 :  "=&r" (dummy), "=r" (result), "=p" (addr)
494                 :  "r" (new_val), "r" (old), "2"(addr)
495                 : "cr0","memory");
496             return (GC_bool) result;
497 #         endif
498         }
499 #       else
500         /* Returns TRUE if the comparison succeeded. */
501         inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
502             GC_word old, GC_word new_val) 
503         {
504 #         if HAS___SYNC_BOOL_COMPARE_AND_SWAP
505             return __sync_bool_compare_and_swap(addr, old, new_val);
506 #         else
507             int result, dummy;
508             __asm__ __volatile__(
509                 "1:\tlwarx %0,0,%5\n"
510                   "\tcmpw %0,%4\n"
511                   "\tbne  2f\n"
512                   "\tstwcx. %3,0,%2\n"
513                   "\tbne- 1b\n"
514                   "\tsync\n"
515                   "\tli %1, 1\n"
516                   "\tb 3f\n"
517                 "2:\tli %1, 0\n"
518                 "3:\t\n"
519                 :  "=&r" (dummy), "=r" (result), "=p" (addr)
520                 :  "r" (new_val), "r" (old), "2"(addr)
521                 : "cr0","memory");
522             return (GC_bool) result;
523 #         endif
524         }
525 #       endif
526 #      endif /* !GENERIC_COMPARE_AND_SWAP */
527         inline static void GC_memory_barrier()
528         {
529             __asm__ __volatile__("sync" : : : "memory");
530         }
531 #     endif /* POWERPC */
532
533 #     if defined(SPARC)
534 #      if !defined(GENERIC_COMPARE_AND_SWAP)
535 #       if CPP_WORDSZ == 64
536         /* Returns TRUE if the comparison succeeded. */
537         inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
538             GC_word old, GC_word new_val)
539         {
540             unsigned long result;
541             __asm__ __volatile__(
542                "casx [%2], %3, %0"
543                 :  "=r" (result)
544                 :  "0" (new_val), "r" (addr), "r" (old)
545                 : "memory");
546             return (GC_bool) (result == old);
547         }
548 #       else
549         /* Returns TRUE if the comparison succeeded. */
550         inline static GC_bool GC_compare_and_exchange(volatile GC_word *_addr,
551             GC_word _old, GC_word _new_val)
552         {
553            register unsigned long result asm("o0");
554            register unsigned long old asm("o1");
555            register volatile GC_word *addr asm("o2");
556            result = _new_val;
557            old = _old;
558            addr = _addr;
559             __asm__ __volatile__(
560                /* We encode the instruction directly so that it
561                   doesn't taint the whole binary as v9-only.  */
562                ".word 0xd1e29009" /* cas [%o2], %o1, %o0 */
563                 :  "=r" (result)
564                 :  "0" (result), "r" (addr), "r"(old)
565                 : "memory");
566             return (GC_bool) (result == old);
567         }
568 #       endif
569 #      endif /* !GENERIC_COMPARE_AND_SWAP */
570         inline static void GC_memory_barrier()
571         {
572            /* All sparc v9 chips provice procesor consistent ordering. */
573            /* Thus a compiler barrier should suffice.                  */
574             __asm__ __volatile__("" : : : "memory");
575         }
576 #     endif /* SPARC */
577
578 #     if defined(IA64)
579 #      if !defined(GENERIC_COMPARE_AND_SWAP)
580          inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
581                                                        GC_word old, GC_word new_val) 
582          {
583           unsigned long oldval;
584 #         if CPP_WORDSZ == 32
585             __asm__ __volatile__(
586                   "addp4 %0=0,%1\n"
587                   "mov ar.ccv=%3 ;; cmpxchg4.rel %0=[%0],%2,ar.ccv"
588                   : "=&r"(oldval)
589                   : "r"(addr), "r"(new_val), "r"(old) : "memory");
590 #         else
591             __asm__ __volatile__(
592                   "mov ar.ccv=%3 ;; cmpxchg8.rel %0=[%1],%2,ar.ccv"
593                   : "=r"(oldval)
594                   : "r"(addr), "r"(new_val), "r"(old) : "memory");
595 #         endif
596           return (oldval == old);
597          }
598 #      endif /* !GENERIC_COMPARE_AND_SWAP */
599 #      if 0
600         /* Shouldn't be needed; we use volatile stores instead. */
601         inline static void GC_memory_barrier()
602         {
603           __asm__ __volatile__("mf" : : : "memory");
604         }
605 #      endif /* 0 */
606 #     endif /* IA64 */
607 #     if defined(ALPHA)
608 #      if !defined(GENERIC_COMPARE_AND_SWAP)
609 #        if defined(__GNUC__)
610            inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
611                                                          GC_word old, GC_word new_val) 
612            {
613              unsigned long was_equal;
614              unsigned long temp;
615
616              __asm__ __volatile__(
617                              "1:     ldq_l %0,%1\n"
618                              "       cmpeq %0,%4,%2\n"
619                              "       mov %3,%0\n"
620                              "       beq %2,2f\n"
621                              "       stq_c %0,%1\n"
622                              "       beq %0,1b\n"
623                              "2:\n"
624                              "       mb\n"
625                              :"=&r" (temp), "=m" (*addr), "=&r" (was_equal)
626                              : "r" (new_val), "Ir" (old)
627                              :"memory");
628              return was_equal;
629            }
630 #        else /* !__GNUC__ */
631            inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
632                                                          GC_word old, GC_word new_val) 
633           {
634             return __CMP_STORE_QUAD(addr, old, new_val, addr);
635           }
636 #        endif /* !__GNUC__ */
637 #      endif /* !GENERIC_COMPARE_AND_SWAP */
638 #      ifdef __GNUC__
639          inline static void GC_memory_barrier()
640          {
641            __asm__ __volatile__("mb" : : : "memory");
642          }
643 #      else
644 #        define GC_memory_barrier() asm("mb")
645 #      endif /* !__GNUC__ */
646 #     endif /* ALPHA */
647 #     if defined(S390)
648 #      if !defined(GENERIC_COMPARE_AND_SWAP)
649          inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
650                                          GC_word old, GC_word new_val)
651          {
652            int retval;
653            __asm__ __volatile__ (
654 #            ifndef __s390x__
655                "     cs  %1,%2,0(%3)\n"
656 #            else
657                "     csg %1,%2,0(%3)\n"
658 #            endif
659              "     ipm %0\n"
660              "     srl %0,28\n"
661              : "=&d" (retval), "+d" (old)
662              : "d" (new_val), "a" (addr)
663              : "cc", "memory");
664            return retval == 0;
665          }
666 #      endif
667 #      define GC_memory_barrier()
668 #     endif
669 #     if !defined(GENERIC_COMPARE_AND_SWAP)
670         /* Returns the original value of *addr. */
671         inline static GC_word GC_atomic_add(volatile GC_word *addr,
672                                             GC_word how_much)
673         {
674           GC_word old;
675           do {
676             old = *addr;
677           } while (!GC_compare_and_exchange(addr, old, old+how_much));
678           return old;
679         }
680 #     else /* GENERIC_COMPARE_AND_SWAP */
681         /* So long as a GC_word can be atomically updated, it should    */
682         /* be OK to read *addr without a lock.                          */
683         extern GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much);
684 #     endif /* GENERIC_COMPARE_AND_SWAP */
685
686 #    endif /* PARALLEL_MARK */
687
688 #    if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS)
689       /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to   */
690       /* be held for long periods, if it is held at all.  Thus spinning */
691       /* and sleeping for fixed periods are likely to result in         */
692       /* significant wasted time.  We thus rely mostly on queued locks. */
693 #     define USE_SPIN_LOCK
694       extern volatile unsigned int GC_allocate_lock;
695       extern void GC_lock(void);
696         /* Allocation lock holder.  Only set if acquired by client through */
697         /* GC_call_with_alloc_lock.                                        */
698 #     ifdef GC_ASSERTIONS
699 #        define LOCK() \
700                 { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); \
701                   SET_LOCK_HOLDER(); }
702 #        define UNLOCK() \
703                 { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
704                   GC_clear(&GC_allocate_lock); }
705 #     else
706 #        define LOCK() \
707                 { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); }
708 #        define UNLOCK() \
709                 GC_clear(&GC_allocate_lock)
710 #     endif /* !GC_ASSERTIONS */
711 #     if 0
712         /* Another alternative for OSF1 might be:               */
713 #       include <sys/mman.h>
714         extern msemaphore GC_allocate_semaphore;
715 #       define LOCK() { if (msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) \
716                             != 0) GC_lock(); else GC_allocate_lock = 1; }
717         /* The following is INCORRECT, since the memory model is too weak. */
718         /* Is this true?  Presumably msem_unlock has the right semantics?  */
719         /*              - HB                                               */
720 #       define UNLOCK() { GC_allocate_lock = 0; \
721                           msem_unlock(&GC_allocate_semaphore, 0); }
722 #     endif /* 0 */
723 #    else /* THREAD_LOCAL_ALLOC  || USE_PTHREAD_LOCKS */
724 #      ifndef USE_PTHREAD_LOCKS
725 #        define USE_PTHREAD_LOCKS
726 #      endif
727 #    endif /* THREAD_LOCAL_ALLOC */
728 #   ifdef USE_PTHREAD_LOCKS
729 #      include <pthread.h>
730        extern pthread_mutex_t GC_allocate_ml;
731 #      ifdef GC_ASSERTIONS
732 #        define LOCK() \
733                 { GC_lock(); \
734                   SET_LOCK_HOLDER(); }
735 #        define UNLOCK() \
736                 { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
737                   pthread_mutex_unlock(&GC_allocate_ml); }
738 #      else /* !GC_ASSERTIONS */
739 #        if defined(NO_PTHREAD_TRYLOCK)
740 #          define LOCK() GC_lock();
741 #        else /* !defined(NO_PTHREAD_TRYLOCK) */
742 #        define LOCK() \
743            { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); }
744 #        endif
745 #        define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
746 #      endif /* !GC_ASSERTIONS */
747 #   endif /* USE_PTHREAD_LOCKS */
748 #   define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
749 #   define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
750 #   define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
751     extern VOLATILE GC_bool GC_collecting;
752 #   define ENTER_GC() GC_collecting = 1;
753 #   define EXIT_GC() GC_collecting = 0;
754     extern void GC_lock(void);
755     extern pthread_t GC_lock_holder;
756 #   ifdef GC_ASSERTIONS
757       extern pthread_t GC_mark_lock_holder;
758 #   endif
759 #  endif /* GC_PTHREADS with linux_threads.c implementation */
760 #  if defined(GC_WIN32_THREADS)
761 #    if defined(GC_PTHREADS)
762 #      include <pthread.h>
763        extern pthread_mutex_t GC_allocate_ml;
764 #      define LOCK()   pthread_mutex_lock(&GC_allocate_ml)
765 #      define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
766 #    else
767 #      include <windows.h>
768        GC_API CRITICAL_SECTION GC_allocate_ml;
769 #      define LOCK() EnterCriticalSection(&GC_allocate_ml);
770 #      define UNLOCK() LeaveCriticalSection(&GC_allocate_ml);
771 #    endif
772 #  endif
773 #  ifndef SET_LOCK_HOLDER
774 #      define SET_LOCK_HOLDER()
775 #      define UNSET_LOCK_HOLDER()
776 #      define I_HOLD_LOCK() FALSE
777                 /* Used on platforms were locks can be reacquired,      */
778                 /* so it doesn't matter if we lie.                      */
779 #  endif
780 # else /* !THREADS */
781 #    define LOCK()
782 #    define UNLOCK()
783 # endif /* !THREADS */
784 # ifndef SET_LOCK_HOLDER
785 #   define SET_LOCK_HOLDER()
786 #   define UNSET_LOCK_HOLDER()
787 #   define I_HOLD_LOCK() FALSE
788                 /* Used on platforms were locks can be reacquired,      */
789                 /* so it doesn't matter if we lie.                      */
790 # endif
791 # ifndef ENTER_GC
792 #   define ENTER_GC()
793 #   define EXIT_GC()
794 # endif
795
796 # ifndef DCL_LOCK_STATE
797 #   define DCL_LOCK_STATE
798 # endif
799 # ifndef FASTLOCK
800 #   define FASTLOCK() LOCK()
801 #   define FASTLOCK_SUCCEEDED() TRUE
802 #   define FASTUNLOCK() UNLOCK()
803 # endif
804
805 #endif /* GC_LOCKS_H */