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
28void
29pg_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
44void
45pg_extern_compiler_barrier(void)
46{
47 /* do nothing */
48}
49#endif
50
51
52#ifdef PG_HAVE_ATOMIC_FLAG_SIMULATION
53
54void
55pg_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
75bool
76pg_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
88void
89pg_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
96bool
97pg_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
105void
106pg_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
123void
124pg_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
136bool
137pg_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
164uint32
165pg_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
181void
182pg_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
199bool
200pg_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
227uint64
228pg_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