13076128985c5077be527dc1ea6fb58072170d96
[cacao.git] / src / mm / boehm-gc / libatomic_ops-1.2 / 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
37 #undef AO_REQUIRE_CAS
38
39 #include <pthread.h>
40 #include <signal.h>
41 #ifdef _HPUX_SOURCE
42 # include <sys/time.h>
43 #else
44 # include <sys/select.h>
45 #endif
46 #include "atomic_ops.h"  /* Without cas emulation! */
47
48 #ifndef AO_HAVE_double_t
49 # include "atomic_ops/sysdeps/standard_ao_double_t.h"
50 #endif
51
52 /*
53  * Lock for pthreads-based implementation.
54  */
55
56 pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER;
57
58 /*
59  * Out of line compare-and-swap emulation based on test and set.
60  * 
61  * We use a small table of locks for different compare_and_swap locations.
62  * Before we update perform a compare-and-swap, we grab the corresponding
63  * lock.  Different locations may hash to the same lock, but since we
64  * never acquire more than one lock at a time, this can't deadlock.
65  * We explicitly disable signals while we perform this operation.
66  *
67  * FIXME: We should probably also support emulation based on Lamport
68  * locks, since we may not have test_and_set either.
69  */
70 #define AO_HASH_SIZE 16
71
72 #define AO_HASH(x) (((unsigned long)(x) >> 12) & (AO_HASH_SIZE-1))
73
74 AO_TS_t AO_locks[AO_HASH_SIZE] = {
75         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
76         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
77         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
78         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
79         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
80         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
81         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
82         AO_TS_INITIALIZER, AO_TS_INITIALIZER,
83 };
84
85 static AO_T dummy = 1;
86
87 /* Spin for 2**n units. */
88 void AO_spin(int n)
89 {
90   int i;
91   AO_T j = AO_load(&dummy);
92
93   for (i = 0; i < (2 << n); ++i)
94     {
95        j *= 5;
96        j -= 4;
97     }
98   AO_store(&dummy, j);
99 }
100
101 void AO_pause(int n)
102 {
103     if (n < 12)
104       AO_spin(n);
105     else
106       {
107         struct timeval tv;
108
109         /* Short async-signal-safe sleep. */
110         tv.tv_sec = 0;
111         tv.tv_usec = (n > 28? 100000 : (1 << (n - 12)));
112         select(0, 0, 0, 0, &tv);
113       }
114 }
115
116 static void lock_ool(volatile AO_TS_t *l)
117 {
118   int i = 0;
119
120   while (AO_test_and_set_acquire(l) == AO_TS_SET)
121     AO_pause(++i);
122 }
123
124 AO_INLINE void lock(volatile AO_TS_t *l)
125 {
126   if (AO_test_and_set_acquire(l) == AO_TS_SET)
127     lock_ool(l);
128 }
129
130 AO_INLINE void unlock(volatile AO_TS_t *l)
131 {
132   AO_CLEAR(l);
133 }
134
135 static sigset_t all_sigs;
136
137 static volatile AO_t initialized = 0;
138
139 static volatile AO_TS_t init_lock = AO_TS_INITIALIZER;
140
141 int AO_compare_and_swap_emulation(volatile AO_t *addr, AO_t old,
142                                   AO_t new_val)
143 {
144   AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
145   sigset_t old_sigs;
146   int result;
147
148   if (!AO_load_acquire(&initialized))
149     {
150       lock(&init_lock);
151       if (!initialized) sigfillset(&all_sigs);
152       unlock(&init_lock);
153       AO_store_release(&initialized, 1);
154     }
155   sigprocmask(SIG_BLOCK, &all_sigs, &old_sigs);
156         /* Neither sigprocmask nor pthread_sigmask is 100%      */
157         /* guaranteed to work here.  Sigprocmask is not         */
158         /* guaranteed be thread safe, and pthread_sigmask       */
159         /* is not async-signal-safe.  Under linuxthreads,       */
160         /* sigprocmask may block some pthreads-internal         */
161         /* signals.  So long as we do that for short periods,   */
162         /* we should be OK.                                     */
163   lock(my_lock);
164   if (*addr == old)
165     {
166       *addr = new_val;
167       result = 1;
168     }
169   else
170     result = 0;
171   unlock(my_lock);
172   sigprocmask(SIG_SETMASK, &old_sigs, NULL);
173   return result;
174 }
175
176 int AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr,
177                                                 AO_t old_val1, AO_t old_val2,
178                                                 AO_t new_val1, AO_t new_val2)
179 {
180   AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
181   sigset_t old_sigs;
182   int result;
183
184   if (!AO_load_acquire(&initialized))
185     {
186       lock(&init_lock);
187       if (!initialized) sigfillset(&all_sigs);
188       unlock(&init_lock);
189       AO_store_release(&initialized, 1);
190     }
191   sigprocmask(SIG_BLOCK, &all_sigs, &old_sigs);
192         /* Neither sigprocmask nor pthread_sigmask is 100%      */
193         /* guaranteed to work here.  Sigprocmask is not         */
194         /* guaranteed be thread safe, and pthread_sigmask       */
195         /* is not async-signal-safe.  Under linuxthreads,       */
196         /* sigprocmask may block some pthreads-internal         */
197         /* signals.  So long as we do that for short periods,   */
198         /* we should be OK.                                     */
199   lock(my_lock);
200   if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2)
201     {
202       addr -> AO_val1 = new_val1;
203       addr -> AO_val2 = new_val2;
204       result = 1;
205     }
206   else
207     result = 0;
208   unlock(my_lock);
209   sigprocmask(SIG_SETMASK, &old_sigs, NULL);
210   return result;
211 }
212
213 void AO_store_full_emulation(volatile AO_t *addr, AO_t val)
214 {
215   AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
216   lock(my_lock);
217   *addr = val;
218   unlock(my_lock);
219 }
220
221 #else /* Non-posix platform */
222
223 int AO_non_posix_implementation_is_entirely_in_headers;
224
225 #endif