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