Upgrade Boehm GC to 7.2alpha4.
[cacao.git] / src / mm / boehm-gc / libatomic_ops / src / atomic_ops.c
1 /*
2  * Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22
23 /*
24  * Initialized data and out-of-line functions to support atomic_ops.h
25  * go here.  Currently this is needed only for pthread-based atomics
26  * emulation, or for compare-and-swap emulation.
27  * Pthreads emulation isn't useful on a native Windows platform, and
28  * cas emulation is not needed.  Thus we skip this on Windows.
29  */
30
31 #if defined(HAVE_CONFIG_H)
32 # include "config.h"
33 #endif
34
35 #if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) \
36     || defined(AO_USE_WIN32_PTHREADS)
37
38 #undef AO_REQUIRE_CAS
39
40 #include <pthread.h>
41
42 #ifdef AO_USE_WIN32_PTHREADS
43 # include <windows.h> /* for Sleep() */
44 #else
45 # include <signal.h>
46 # ifdef _HPUX_SOURCE
47 #   include <sys/time.h>
48 # else
49 #   include <sys/select.h>
50 # endif
51 #endif
52
53 #include "atomic_ops.h"  /* Without cas emulation! */
54
55 #ifndef AO_HAVE_double_t
56 # include "atomic_ops/sysdeps/standard_ao_double_t.h"
57 #endif
58
59 /*
60  * Lock for pthreads-based implementation.
61  */
62
63 pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER;
64
65 /*
66  * Out of line compare-and-swap emulation based on test and set.
67  *
68  * We use a small table of locks for different compare_and_swap locations.
69  * Before we update perform a compare-and-swap, we grab the corresponding
70  * lock.  Different locations may hash to the same lock, but since we
71  * never acquire more than one lock at a time, this can't deadlock.
72  * We explicitly disable signals while we perform this operation.
73  *
74  * FIXME: We should probably also support emulation based on Lamport
75  * locks, since we may not have test_and_set either.
76  */
77 #define AO_HASH_SIZE 16
78
79 #define AO_HASH(x) (((unsigned long)(x) >> 12) & (AO_HASH_SIZE-1))
80
81 AO_TS_t AO_locks[AO_HASH_SIZE] = {
82         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
83         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
84         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
85         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
86         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
87         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
88         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
89         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
90 };
91
92 static AO_T dummy = 1;
93
94 /* Spin for 2**n units. */
95 void AO_spin(int n)
96 {
97   int i;
98   AO_T j = AO_load(&dummy);
99
100   for (i = 0; i < (2 << n); ++i)
101     {
102        j *= 5;
103        j -= 4;
104     }
105   AO_store(&dummy, j);
106 }
107
108 void AO_pause(int n)
109 {
110     if (n < 12)
111       AO_spin(n);
112     else
113       {
114 #     ifdef AO_USE_WIN32_PTHREADS
115         Sleep(n > 28 ? 100 : 1 << (n - 22)); /* in millis */
116 #     else
117         struct timeval tv;
118
119         /* Short async-signal-safe sleep. */
120         tv.tv_sec = 0;
121         tv.tv_usec = (n > 28? 100000 : (1 << (n - 12)));
122         select(0, 0, 0, 0, &tv);
123 #     endif
124       }
125 }
126
127 static void lock_ool(volatile AO_TS_t *l)
128 {
129   int i = 0;
130
131   while (AO_test_and_set_acquire(l) == AO_TS_SET)
132     AO_pause(++i);
133 }
134
135 AO_INLINE void lock(volatile AO_TS_t *l)
136 {
137   if (AO_test_and_set_acquire(l) == AO_TS_SET)
138     lock_ool(l);
139 }
140
141 AO_INLINE void unlock(volatile AO_TS_t *l)
142 {
143   AO_CLEAR(l);
144 }
145
146 #ifndef AO_USE_WIN32_PTHREADS
147   static sigset_t all_sigs;
148   static volatile AO_t initialized = 0;
149 #endif
150
151 static volatile AO_TS_t init_lock = AO_TS_INITIALIZER;
152
153 int AO_compare_and_swap_emulation(volatile AO_t *addr, AO_t old,
154                                   AO_t new_val)
155 {
156   AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
157   int result;
158
159 # ifndef AO_USE_WIN32_PTHREADS
160     sigset_t old_sigs;
161     if (!AO_load_acquire(&initialized))
162     {
163       lock(&init_lock);
164       if (!initialized) sigfillset(&all_sigs);
165       unlock(&init_lock);
166       AO_store_release(&initialized, 1);
167     }
168     sigprocmask(SIG_BLOCK, &all_sigs, &old_sigs);
169         /* Neither sigprocmask nor pthread_sigmask is 100%      */
170         /* guaranteed to work here.  Sigprocmask is not         */
171         /* guaranteed be thread safe, and pthread_sigmask       */
172         /* is not async-signal-safe.  Under linuxthreads,       */
173         /* sigprocmask may block some pthreads-internal         */
174         /* signals.  So long as we do that for short periods,   */
175         /* we should be OK.                                     */
176 # endif
177   lock(my_lock);
178   if (*addr == old)
179     {
180       *addr = new_val;
181       result = 1;
182     }
183   else
184     result = 0;
185   unlock(my_lock);
186 # ifndef AO_USE_WIN32_PTHREADS
187     sigprocmask(SIG_SETMASK, &old_sigs, NULL);
188 # endif
189   return result;
190 }
191
192 int AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr,
193                                                 AO_t old_val1, AO_t old_val2,
194                                                 AO_t new_val1, AO_t new_val2)
195 {
196   AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
197   int result;
198
199 # ifndef AO_USE_WIN32_PTHREADS
200     sigset_t old_sigs;
201     if (!AO_load_acquire(&initialized))
202     {
203       lock(&init_lock);
204       if (!initialized) sigfillset(&all_sigs);
205       unlock(&init_lock);
206       AO_store_release(&initialized, 1);
207     }
208     sigprocmask(SIG_BLOCK, &all_sigs, &old_sigs);
209         /* Neither sigprocmask nor pthread_sigmask is 100%      */
210         /* guaranteed to work here.  Sigprocmask is not         */
211         /* guaranteed be thread safe, and pthread_sigmask       */
212         /* is not async-signal-safe.  Under linuxthreads,       */
213         /* sigprocmask may block some pthreads-internal         */
214         /* signals.  So long as we do that for short periods,   */
215         /* we should be OK.                                     */
216 # endif
217   lock(my_lock);
218   if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2)
219     {
220       addr -> AO_val1 = new_val1;
221       addr -> AO_val2 = new_val2;
222       result = 1;
223     }
224   else
225     result = 0;
226   unlock(my_lock);
227 # ifndef AO_USE_WIN32_PTHREADS
228     sigprocmask(SIG_SETMASK, &old_sigs, NULL);
229 # endif
230   return result;
231 }
232
233 void AO_store_full_emulation(volatile AO_t *addr, AO_t val)
234 {
235   AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
236   lock(my_lock);
237   *addr = val;
238   unlock(my_lock);
239 }
240
241 #else /* Non-posix platform */
242
243 int AO_non_posix_implementation_is_entirely_in_headers;
244
245 #endif