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