summaryrefslogtreecommitdiff
path: root/include/private/gc_locks.h
blob: 3b0d1c3a2627111efd6a7fad75080b8f86ee83ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
 * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
 * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
 *
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

#ifndef GC_LOCKS_H
#define GC_LOCKS_H

/*
 * Mutual exclusion between allocator/collector routines.
 * Needed if there is more than one allocator thread.
 * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK.
 *
 * Note that I_HOLD_LOCK and I_DONT_HOLD_LOCK are used only positively
 * in assertions, and may return TRUE in the "don't know" case.
 */
# ifdef THREADS

#  if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
#    include "atomic_ops.h"
#  endif

   GC_API void GC_CALL GC_noop1(word);
#  ifdef PCR
#    include <base/PCR_Base.h>
#    include <th/PCR_Th.h>
     GC_EXTERN PCR_Th_ML GC_allocate_ml;
#    define DCL_LOCK_STATE \
         PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask
#    define UNCOND_LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
#    define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
#  endif

#  if !defined(AO_HAVE_test_and_set_acquire) && defined(GC_PTHREADS)
#    define USE_PTHREAD_LOCKS
#  endif

#  if defined(GC_WIN32_THREADS) && defined(GC_PTHREADS)
#    define USE_PTHREAD_LOCKS
#  endif

#  if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS)
#    include <windows.h>
#    define NO_THREAD (DWORD)(-1)
     GC_EXTERN DWORD GC_lock_holder;
     GC_EXTERN CRITICAL_SECTION GC_allocate_ml;
#    ifdef GC_ASSERTIONS
#        define UNCOND_LOCK() \
                { EnterCriticalSection(&GC_allocate_ml); \
                  SET_LOCK_HOLDER(); }
#        define UNCOND_UNLOCK() \
                { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
                  LeaveCriticalSection(&GC_allocate_ml); }
#    else
#      define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml);
#      define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml);
#    endif /* !GC_ASSERTIONS */
#    define SET_LOCK_HOLDER() GC_lock_holder = GetCurrentThreadId()
#    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
#    define I_HOLD_LOCK() (!GC_need_to_lock \
                           || GC_lock_holder == GetCurrentThreadId())
#    define I_DONT_HOLD_LOCK() (!GC_need_to_lock \
                           || GC_lock_holder != GetCurrentThreadId())
#  elif defined(SN_TARGET_PS3)
#    include <pthread.h>
     GC_EXTERN pthread_mutex_t GC_allocate_ml;
#    define LOCK() pthread_mutex_lock(&GC_allocate_ml)
#    define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
#  elif defined(GC_PTHREADS)
#    include <pthread.h>

     /* Posix allows pthread_t to be a struct, though it rarely is.     */
     /* Unfortunately, we need to use a pthread_t to index a data       */
     /* structure.  It also helps if comparisons don't involve a        */
     /* function call.  Hence we introduce platform-dependent macros    */
     /* to compare pthread_t ids and to map them to integers.           */
     /* the mapping to integers does not need to result in different    */
     /* integers for each thread, though that should be true as much    */
     /* as possible.                                                    */
     /* Refine to exclude platforms on which pthread_t is struct */
#    if !defined(GC_WIN32_PTHREADS)
#      define NUMERIC_THREAD_ID(id) ((unsigned long)(id))
#      define THREAD_EQUAL(id1, id2) ((id1) == (id2))
#      define NUMERIC_THREAD_ID_UNIQUE
#    else
#      if defined(GC_WIN32_PTHREADS)
#        define NUMERIC_THREAD_ID(id) ((unsigned long)(id.p))
         /* Using documented internal details of win32_pthread library. */
         /* Faster than pthread_equal(). Should not change with         */
         /* future versions of win32_pthread library.                   */
#        define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x))
#        undef NUMERIC_THREAD_ID_UNIQUE
#      else
         /* Generic definitions that always work, but will result in    */
         /* poor performance and weak assertion checking.               */
#        define NUMERIC_THREAD_ID(id) 1l
#        define THREAD_EQUAL(id1, id2) pthread_equal(id1, id2)
#        undef NUMERIC_THREAD_ID_UNIQUE
#      endif
#    endif
#    define NO_THREAD ((unsigned long)(-1l))
                /* != NUMERIC_THREAD_ID(pthread_self()) for any thread */

#    if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS)
      /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to   */
      /* be held for long periods, if it is held at all.  Thus spinning */
      /* and sleeping for fixed periods are likely to result in         */
      /* significant wasted time.  We thus rely mostly on queued locks. */
#     define USE_SPIN_LOCK
      GC_EXTERN volatile AO_TS_t GC_allocate_lock;
      GC_INNER void GC_lock(void);
        /* Allocation lock holder.  Only set if acquired by client through */
        /* GC_call_with_alloc_lock.                                        */
#     ifdef GC_ASSERTIONS
#        define UNCOND_LOCK() \
                { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
                        GC_lock(); \
                  SET_LOCK_HOLDER(); }
#        define UNCOND_UNLOCK() \
                { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
                  AO_CLEAR(&GC_allocate_lock); }
#     else
#        define UNCOND_LOCK() \
                { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
                        GC_lock(); }
#        define UNCOND_UNLOCK() \
                AO_CLEAR(&GC_allocate_lock)
#     endif /* !GC_ASSERTIONS */
#    else /* THREAD_LOCAL_ALLOC  || USE_PTHREAD_LOCKS */
#      ifndef USE_PTHREAD_LOCKS
#        define USE_PTHREAD_LOCKS
#      endif
#    endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCK */
#    ifdef USE_PTHREAD_LOCKS
#      include <pthread.h>
       GC_EXTERN pthread_mutex_t GC_allocate_ml;
#      ifdef GC_ASSERTIONS
#        define UNCOND_LOCK() \
                { GC_lock(); \
                  SET_LOCK_HOLDER(); }
#        define UNCOND_UNLOCK() \
                { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
                  pthread_mutex_unlock(&GC_allocate_ml); }
#      else /* !GC_ASSERTIONS */
#        if defined(NO_PTHREAD_TRYLOCK)
#          define UNCOND_LOCK() GC_lock();
#        else /* !defined(NO_PTHREAD_TRYLOCK) */
#        define UNCOND_LOCK() \
           { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); }
#        endif
#        define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
#      endif /* !GC_ASSERTIONS */
#    endif /* USE_PTHREAD_LOCKS */
#    define SET_LOCK_HOLDER() \
                GC_lock_holder = NUMERIC_THREAD_ID(pthread_self())
#    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
#    define I_HOLD_LOCK() \
                (!GC_need_to_lock || \
                 GC_lock_holder == NUMERIC_THREAD_ID(pthread_self()))
#    ifndef NUMERIC_THREAD_ID_UNIQUE
#      define I_DONT_HOLD_LOCK() 1  /* Conservatively say yes */
#    else
#      define I_DONT_HOLD_LOCK() \
                (!GC_need_to_lock \
                 || GC_lock_holder != NUMERIC_THREAD_ID(pthread_self()))
#    endif
     GC_EXTERN volatile GC_bool GC_collecting;
#    define ENTER_GC() GC_collecting = 1;
#    define EXIT_GC() GC_collecting = 0;
     GC_INNER void GC_lock(void);
     GC_EXTERN unsigned long GC_lock_holder;
#    if defined(GC_ASSERTIONS) && defined(PARALLEL_MARK)
       GC_EXTERN unsigned long GC_mark_lock_holder;
#    endif
#  endif /* GC_PTHREADS with linux_threads.c implementation */
   GC_EXTERN GC_bool GC_need_to_lock;

# else /* !THREADS */
#   define LOCK()
#   define UNLOCK()
#   define SET_LOCK_HOLDER()
#   define UNSET_LOCK_HOLDER()
#   define I_HOLD_LOCK() TRUE
#   define I_DONT_HOLD_LOCK() TRUE
                /* Used only in positive assertions or to test whether  */
                /* we still need to acquire the lock.  TRUE works in    */
                /* either case.                                         */
# endif /* !THREADS */

#if defined(UNCOND_LOCK) && !defined(LOCK)
                /* At least two thread running; need to lock.   */
#    define LOCK() if (GC_need_to_lock) { UNCOND_LOCK(); }
#    define UNLOCK() if (GC_need_to_lock) { UNCOND_UNLOCK(); }
#endif

# ifndef ENTER_GC
#   define ENTER_GC()
#   define EXIT_GC()
# endif

# ifndef DCL_LOCK_STATE
#   define DCL_LOCK_STATE
# endif

#endif /* GC_LOCKS_H */