Line data Source code
1 : /*
2 : * Copyright (c) 2003-2011 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 : #undef AO_REQUIRE_CAS
47 : #include "atomic_ops.h" /* Without cas emulation! */
48 :
49 : #if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) \
50 : || defined(AO_USE_NO_SIGNALS)
51 :
52 : #ifndef AO_NO_PTHREADS
53 : # include <pthread.h>
54 : #endif
55 :
56 : #ifndef AO_USE_NO_SIGNALS
57 : # include <signal.h>
58 : #endif
59 :
60 : #ifdef AO_USE_NANOSLEEP
61 : /* This requires _POSIX_TIMERS feature. */
62 : # include <sys/time.h>
63 : # include <time.h>
64 : #elif defined(AO_USE_WIN32_PTHREADS)
65 : # include <windows.h> /* for Sleep() */
66 : #elif defined(_HPUX_SOURCE)
67 : # include <sys/time.h>
68 : #else
69 : # include <sys/select.h>
70 : #endif
71 :
72 : #ifndef AO_HAVE_double_t
73 : # include "atomic_ops/sysdeps/standard_ao_double_t.h"
74 : #endif
75 :
76 : /* Lock for pthreads-based implementation. */
77 : #ifndef AO_NO_PTHREADS
78 : pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER;
79 : #endif
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 : * TODO: 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 : void AO_pause(int); /* defined below */
105 :
106 0 : static void lock_ool(volatile AO_TS_t *l)
107 : {
108 0 : int i = 0;
109 :
110 0 : while (AO_test_and_set_acquire(l) == AO_TS_SET)
111 0 : AO_pause(++i);
112 0 : }
113 :
114 0 : AO_INLINE void lock(volatile AO_TS_t *l)
115 : {
116 0 : if (AO_EXPECT_FALSE(AO_test_and_set_acquire(l) == AO_TS_SET))
117 0 : lock_ool(l);
118 0 : }
119 :
120 0 : AO_INLINE void unlock(volatile AO_TS_t *l)
121 : {
122 0 : AO_CLEAR(l);
123 0 : }
124 :
125 : #ifndef AO_USE_NO_SIGNALS
126 : static sigset_t all_sigs;
127 : static volatile AO_t initialized = 0;
128 : static volatile AO_TS_t init_lock = AO_TS_INITIALIZER;
129 :
130 0 : AO_INLINE void block_all_signals(sigset_t *old_sigs_ptr)
131 : {
132 0 : if (AO_EXPECT_FALSE(!AO_load_acquire(&initialized)))
133 : {
134 0 : lock(&init_lock);
135 0 : if (!initialized)
136 0 : sigfillset(&all_sigs);
137 0 : unlock(&init_lock);
138 0 : AO_store_release(&initialized, 1);
139 : }
140 0 : sigprocmask(SIG_BLOCK, &all_sigs, old_sigs_ptr);
141 : /* Neither sigprocmask nor pthread_sigmask is 100% */
142 : /* guaranteed to work here. Sigprocmask is not */
143 : /* guaranteed be thread safe, and pthread_sigmask */
144 : /* is not async-signal-safe. Under linuxthreads, */
145 : /* sigprocmask may block some pthreads-internal */
146 : /* signals. So long as we do that for short periods, */
147 : /* we should be OK. */
148 0 : }
149 : #endif /* !AO_USE_NO_SIGNALS */
150 :
151 0 : AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, AO_t old_val,
152 : AO_t new_val)
153 : {
154 0 : AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
155 : AO_t fetched_val;
156 :
157 : # ifndef AO_USE_NO_SIGNALS
158 : sigset_t old_sigs;
159 0 : block_all_signals(&old_sigs);
160 : # endif
161 0 : lock(my_lock);
162 0 : fetched_val = *addr;
163 0 : if (fetched_val == old_val)
164 0 : *addr = new_val;
165 0 : unlock(my_lock);
166 : # ifndef AO_USE_NO_SIGNALS
167 0 : sigprocmask(SIG_SETMASK, &old_sigs, NULL);
168 : # endif
169 0 : return fetched_val;
170 : }
171 :
172 0 : int AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr,
173 : AO_t old_val1, AO_t old_val2,
174 : AO_t new_val1, AO_t new_val2)
175 : {
176 0 : AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
177 : int result;
178 :
179 : # ifndef AO_USE_NO_SIGNALS
180 : sigset_t old_sigs;
181 0 : block_all_signals(&old_sigs);
182 : # endif
183 0 : lock(my_lock);
184 0 : if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2)
185 : {
186 0 : addr -> AO_val1 = new_val1;
187 0 : addr -> AO_val2 = new_val2;
188 0 : result = 1;
189 : }
190 : else
191 0 : result = 0;
192 0 : unlock(my_lock);
193 : # ifndef AO_USE_NO_SIGNALS
194 0 : sigprocmask(SIG_SETMASK, &old_sigs, NULL);
195 : # endif
196 0 : return result;
197 : }
198 :
199 0 : void AO_store_full_emulation(volatile AO_t *addr, AO_t val)
200 : {
201 0 : AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
202 0 : lock(my_lock);
203 0 : *addr = val;
204 0 : unlock(my_lock);
205 0 : }
206 :
207 : #else /* Non-posix platform */
208 :
209 : # include <windows.h>
210 :
211 : # define AO_USE_WIN32_PTHREADS
212 : /* define to use Sleep() */
213 :
214 : extern int AO_non_posix_implementation_is_entirely_in_headers;
215 :
216 : #endif
217 :
218 : static AO_t spin_dummy = 1;
219 :
220 : /* Spin for 2**n units. */
221 0 : static void AO_spin(int n)
222 : {
223 0 : AO_t j = AO_load(&spin_dummy);
224 0 : int i = 2 << n;
225 :
226 0 : while (i-- > 0)
227 0 : j += (j - 1) << 2;
228 : /* Given 'spin_dummy' is initialized to 1, j is 1 after the loop. */
229 0 : AO_store(&spin_dummy, j);
230 0 : }
231 :
232 0 : void AO_pause(int n)
233 : {
234 0 : if (n < 12)
235 0 : AO_spin(n);
236 : else
237 : {
238 : # ifdef AO_USE_NANOSLEEP
239 : struct timespec ts;
240 : ts.tv_sec = 0;
241 : ts.tv_nsec = (n > 28 ? 100000 * 1000 : 1 << (n - 2));
242 : nanosleep(&ts, 0);
243 : # elif defined(AO_USE_WIN32_PTHREADS)
244 : Sleep(n > 28 ? 100 : n < 22 ? 1 : 1 << (n - 22)); /* in millis */
245 : # else
246 : struct timeval tv;
247 : /* Short async-signal-safe sleep. */
248 0 : tv.tv_sec = 0;
249 0 : tv.tv_usec = n > 28 ? 100000 : 1 << (n - 12);
250 0 : select(0, 0, 0, 0, &tv);
251 : # endif
252 : }
253 0 : }
|