2 * Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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
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.
31 #if defined(HAVE_CONFIG_H)
35 #if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__)
42 # include <sys/time.h>
44 # include <sys/select.h>
46 #include "atomic_ops.h" /* Without cas emulation! */
48 #ifndef AO_HAVE_double_t
49 # include "atomic_ops/sysdeps/standard_ao_double_t.h"
53 * Lock for pthreads-based implementation.
56 pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER;
59 * Out of line compare-and-swap emulation based on test and set.
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.
67 * FIXME: We should probably also support emulation based on Lamport
68 * locks, since we may not have test_and_set either.
70 #define AO_HASH_SIZE 16
72 #define AO_HASH(x) (((unsigned long)(x) >> 12) & (AO_HASH_SIZE-1))
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,
85 static AO_T dummy = 1;
87 /* Spin for 2**n units. */
91 AO_T j = AO_load(&dummy);
93 for (i = 0; i < (2 << n); ++i)
109 /* Short async-signal-safe sleep. */
111 tv.tv_usec = (n > 28? 100000 : (1 << (n - 12)));
112 select(0, 0, 0, 0, &tv);
116 static void lock_ool(volatile AO_TS_t *l)
120 while (AO_test_and_set_acquire(l) == AO_TS_SET)
124 AO_INLINE void lock(volatile AO_TS_t *l)
126 if (AO_test_and_set_acquire(l) == AO_TS_SET)
130 AO_INLINE void unlock(volatile AO_TS_t *l)
135 static sigset_t all_sigs;
137 static volatile AO_t initialized = 0;
139 static volatile AO_TS_t init_lock = AO_TS_INITIALIZER;
141 int AO_compare_and_swap_emulation(volatile AO_t *addr, AO_t old,
144 AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
148 if (!AO_load_acquire(&initialized))
151 if (!initialized) sigfillset(&all_sigs);
153 AO_store_release(&initialized, 1);
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. */
172 sigprocmask(SIG_SETMASK, &old_sigs, NULL);
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)
180 AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
184 if (!AO_load_acquire(&initialized))
187 if (!initialized) sigfillset(&all_sigs);
189 AO_store_release(&initialized, 1);
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. */
200 if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2)
202 addr -> AO_val1 = new_val1;
203 addr -> AO_val2 = new_val2;
209 sigprocmask(SIG_SETMASK, &old_sigs, NULL);
213 void AO_store_full_emulation(volatile AO_t *addr, AO_t val)
215 AO_TS_t *my_lock = AO_locks + AO_HASH(addr);
221 #else /* Non-posix platform */
223 int AO_non_posix_implementation_is_entirely_in_headers;