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