Synopsis - Cross-Reference
File: /src/Synopsis/gc/atomic_ops.c1/* 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 56pthread_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 grap 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 suppport 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 74AO_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 85static AO_T dummy = 1; 86 87/* Spin for 2**n units. */ 88void 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 101void 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 116static 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 124AO_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 130AO_INLINE void unlock(volatile AO_TS_t *l) 131{ 132 AO_CLEAR(l); 133} 134 135static sigset_t all_sigs; 136 137static volatile AO_t initialized = 0; 138 139static volatile AO_TS_t init_lock = AO_TS_INITIALIZER; 140 141int 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 176int 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 213void 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 223int AO_non_posix_implementation_is_entirely_in_headers; 224 225#endif