| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * atomics.c |
| 4 | * Non-Inline parts of the atomics implementation |
| 5 | * |
| 6 | * Portions Copyright (c) 2013-2019, PostgreSQL Global Development Group |
| 7 | * |
| 8 | * |
| 9 | * IDENTIFICATION |
| 10 | * src/backend/port/atomics.c |
| 11 | * |
| 12 | *------------------------------------------------------------------------- |
| 13 | */ |
| 14 | #include "postgres.h" |
| 15 | |
| 16 | #include "miscadmin.h" |
| 17 | #include "port/atomics.h" |
| 18 | #include "storage/spin.h" |
| 19 | |
| 20 | #ifdef PG_HAVE_MEMORY_BARRIER_EMULATION |
| 21 | #ifdef WIN32 |
| 22 | #error "barriers are required (and provided) on WIN32 platforms" |
| 23 | #endif |
| 24 | #include <signal.h> |
| 25 | #endif |
| 26 | |
| 27 | #ifdef PG_HAVE_MEMORY_BARRIER_EMULATION |
| 28 | void |
| 29 | pg_spinlock_barrier(void) |
| 30 | { |
| 31 | /* |
| 32 | * NB: we have to be reentrant here, some barriers are placed in signal |
| 33 | * handlers. |
| 34 | * |
| 35 | * We use kill(0) for the fallback barrier as we assume that kernels on |
| 36 | * systems old enough to require fallback barrier support will include an |
| 37 | * appropriate barrier while checking the existence of the postmaster pid. |
| 38 | */ |
| 39 | (void) kill(PostmasterPid, 0); |
| 40 | } |
| 41 | #endif |
| 42 | |
| 43 | #ifdef PG_HAVE_COMPILER_BARRIER_EMULATION |
| 44 | void |
| 45 | pg_extern_compiler_barrier(void) |
| 46 | { |
| 47 | /* do nothing */ |
| 48 | } |
| 49 | #endif |
| 50 | |
| 51 | |
| 52 | #ifdef PG_HAVE_ATOMIC_FLAG_SIMULATION |
| 53 | |
| 54 | void |
| 55 | pg_atomic_init_flag_impl(volatile pg_atomic_flag *ptr) |
| 56 | { |
| 57 | StaticAssertStmt(sizeof(ptr->sema) >= sizeof(slock_t), |
| 58 | "size mismatch of atomic_flag vs slock_t" ); |
| 59 | |
| 60 | #ifndef HAVE_SPINLOCKS |
| 61 | |
| 62 | /* |
| 63 | * NB: If we're using semaphore based TAS emulation, be careful to use a |
| 64 | * separate set of semaphores. Otherwise we'd get in trouble if an atomic |
| 65 | * var would be manipulated while spinlock is held. |
| 66 | */ |
| 67 | s_init_lock_sema((slock_t *) &ptr->sema, true); |
| 68 | #else |
| 69 | SpinLockInit((slock_t *) &ptr->sema); |
| 70 | #endif |
| 71 | |
| 72 | ptr->value = false; |
| 73 | } |
| 74 | |
| 75 | bool |
| 76 | pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) |
| 77 | { |
| 78 | uint32 oldval; |
| 79 | |
| 80 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 81 | oldval = ptr->value; |
| 82 | ptr->value = true; |
| 83 | SpinLockRelease((slock_t *) &ptr->sema); |
| 84 | |
| 85 | return oldval == 0; |
| 86 | } |
| 87 | |
| 88 | void |
| 89 | pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr) |
| 90 | { |
| 91 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 92 | ptr->value = false; |
| 93 | SpinLockRelease((slock_t *) &ptr->sema); |
| 94 | } |
| 95 | |
| 96 | bool |
| 97 | pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *ptr) |
| 98 | { |
| 99 | return ptr->value == 0; |
| 100 | } |
| 101 | |
| 102 | #endif /* PG_HAVE_ATOMIC_FLAG_SIMULATION */ |
| 103 | |
| 104 | #ifdef PG_HAVE_ATOMIC_U32_SIMULATION |
| 105 | void |
| 106 | pg_atomic_init_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val_) |
| 107 | { |
| 108 | StaticAssertStmt(sizeof(ptr->sema) >= sizeof(slock_t), |
| 109 | "size mismatch of atomic_uint32 vs slock_t" ); |
| 110 | |
| 111 | /* |
| 112 | * If we're using semaphore based atomic flags, be careful about nested |
| 113 | * usage of atomics while a spinlock is held. |
| 114 | */ |
| 115 | #ifndef HAVE_SPINLOCKS |
| 116 | s_init_lock_sema((slock_t *) &ptr->sema, true); |
| 117 | #else |
| 118 | SpinLockInit((slock_t *) &ptr->sema); |
| 119 | #endif |
| 120 | ptr->value = val_; |
| 121 | } |
| 122 | |
| 123 | void |
| 124 | pg_atomic_write_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val) |
| 125 | { |
| 126 | /* |
| 127 | * One might think that an unlocked write doesn't need to acquire the |
| 128 | * spinlock, but one would be wrong. Even an unlocked write has to cause a |
| 129 | * concurrent pg_atomic_compare_exchange_u32() (et al) to fail. |
| 130 | */ |
| 131 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 132 | ptr->value = val; |
| 133 | SpinLockRelease((slock_t *) &ptr->sema); |
| 134 | } |
| 135 | |
| 136 | bool |
| 137 | pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, |
| 138 | uint32 *expected, uint32 newval) |
| 139 | { |
| 140 | bool ret; |
| 141 | |
| 142 | /* |
| 143 | * Do atomic op under a spinlock. It might look like we could just skip |
| 144 | * the cmpxchg if the lock isn't available, but that'd just emulate a |
| 145 | * 'weak' compare and swap. I.e. one that allows spurious failures. Since |
| 146 | * several algorithms rely on a strong variant and that is efficiently |
| 147 | * implementable on most major architectures let's emulate it here as |
| 148 | * well. |
| 149 | */ |
| 150 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 151 | |
| 152 | /* perform compare/exchange logic */ |
| 153 | ret = ptr->value == *expected; |
| 154 | *expected = ptr->value; |
| 155 | if (ret) |
| 156 | ptr->value = newval; |
| 157 | |
| 158 | /* and release lock */ |
| 159 | SpinLockRelease((slock_t *) &ptr->sema); |
| 160 | |
| 161 | return ret; |
| 162 | } |
| 163 | |
| 164 | uint32 |
| 165 | pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) |
| 166 | { |
| 167 | uint32 oldval; |
| 168 | |
| 169 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 170 | oldval = ptr->value; |
| 171 | ptr->value += add_; |
| 172 | SpinLockRelease((slock_t *) &ptr->sema); |
| 173 | return oldval; |
| 174 | } |
| 175 | |
| 176 | #endif /* PG_HAVE_ATOMIC_U32_SIMULATION */ |
| 177 | |
| 178 | |
| 179 | #ifdef PG_HAVE_ATOMIC_U64_SIMULATION |
| 180 | |
| 181 | void |
| 182 | pg_atomic_init_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val_) |
| 183 | { |
| 184 | StaticAssertStmt(sizeof(ptr->sema) >= sizeof(slock_t), |
| 185 | "size mismatch of atomic_uint64 vs slock_t" ); |
| 186 | |
| 187 | /* |
| 188 | * If we're using semaphore based atomic flags, be careful about nested |
| 189 | * usage of atomics while a spinlock is held. |
| 190 | */ |
| 191 | #ifndef HAVE_SPINLOCKS |
| 192 | s_init_lock_sema((slock_t *) &ptr->sema, true); |
| 193 | #else |
| 194 | SpinLockInit((slock_t *) &ptr->sema); |
| 195 | #endif |
| 196 | ptr->value = val_; |
| 197 | } |
| 198 | |
| 199 | bool |
| 200 | pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, |
| 201 | uint64 *expected, uint64 newval) |
| 202 | { |
| 203 | bool ret; |
| 204 | |
| 205 | /* |
| 206 | * Do atomic op under a spinlock. It might look like we could just skip |
| 207 | * the cmpxchg if the lock isn't available, but that'd just emulate a |
| 208 | * 'weak' compare and swap. I.e. one that allows spurious failures. Since |
| 209 | * several algorithms rely on a strong variant and that is efficiently |
| 210 | * implementable on most major architectures let's emulate it here as |
| 211 | * well. |
| 212 | */ |
| 213 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 214 | |
| 215 | /* perform compare/exchange logic */ |
| 216 | ret = ptr->value == *expected; |
| 217 | *expected = ptr->value; |
| 218 | if (ret) |
| 219 | ptr->value = newval; |
| 220 | |
| 221 | /* and release lock */ |
| 222 | SpinLockRelease((slock_t *) &ptr->sema); |
| 223 | |
| 224 | return ret; |
| 225 | } |
| 226 | |
| 227 | uint64 |
| 228 | pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) |
| 229 | { |
| 230 | uint64 oldval; |
| 231 | |
| 232 | SpinLockAcquire((slock_t *) &ptr->sema); |
| 233 | oldval = ptr->value; |
| 234 | ptr->value += add_; |
| 235 | SpinLockRelease((slock_t *) &ptr->sema); |
| 236 | return oldval; |
| 237 | } |
| 238 | |
| 239 | #endif /* PG_HAVE_ATOMIC_U64_SIMULATION */ |
| 240 | |