]> git.proxmox.com Git - mirror_frr.git/blame - lib/seqlock.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / seqlock.c
CommitLineData
47a3a827 1// SPDX-License-Identifier: LGPL-2.1-or-later
440d5faa
DL
2/*
3 * "Sequence" lock primitive
4 *
5 * Copyright (C) 2015 David Lamparter <equinox@diac24.net>
440d5faa
DL
6 */
7
8#define _GNU_SOURCE
9
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
2a5e6235 14#include <string.h>
440d5faa
DL
15#include <unistd.h>
16#include <limits.h>
17#include <errno.h>
18#include <sys/types.h>
19#include <sys/time.h>
20#include <pthread.h>
21#include <assert.h>
22
23#include "seqlock.h"
24
30ef834a
DL
25/****************************************
26 * OS specific synchronization wrappers *
27 ****************************************/
28
29/*
30 * Linux: sys_futex()
31 */
440d5faa 32#ifdef HAVE_SYNC_LINUX_FUTEX
440d5faa
DL
33#include <sys/syscall.h>
34#include <linux/futex.h>
35
2a5e6235
DL
36static long sys_futex(void *addr1, int op, int val1,
37 const struct timespec *timeout, void *addr2, int val3)
440d5faa
DL
38{
39 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
40}
41
42#define wait_once(sqlo, val) \
43 sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
2a5e6235
DL
44#define wait_time(sqlo, val, time, reltime) \
45 sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
46 NULL, ~0U)
440d5faa
DL
47#define wait_poke(sqlo) \
48 sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
49
30ef834a
DL
50/*
51 * OpenBSD: sys_futex(), almost the same as on Linux
52 */
440d5faa 53#elif defined(HAVE_SYNC_OPENBSD_FUTEX)
440d5faa
DL
54#include <sys/syscall.h>
55#include <sys/futex.h>
56
2a5e6235
DL
57#define TIME_RELATIVE 1
58
440d5faa
DL
59#define wait_once(sqlo, val) \
60 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
2a5e6235
DL
61#define wait_time(sqlo, val, time, reltime) \
62 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
440d5faa
DL
63#define wait_poke(sqlo) \
64 futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
65
30ef834a
DL
66/*
67 * FreeBSD: _umtx_op()
68 */
440d5faa 69#elif defined(HAVE_SYNC_UMTX_OP)
440d5faa
DL
70#include <sys/umtx.h>
71
72#define wait_once(sqlo, val) \
73 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
2a5e6235
DL
74static int wait_time(struct seqlock *sqlo, uint32_t val,
75 const struct timespec *abstime,
76 const struct timespec *reltime)
77{
78 struct _umtx_time t;
79 t._flags = UMTX_ABSTIME;
80 t._clockid = CLOCK_MONOTONIC;
81 memcpy(&t._timeout, abstime, sizeof(t._timeout));
82 return _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val,
83 (void *)(uintptr_t) sizeof(t), &t);
84}
440d5faa
DL
85#define wait_poke(sqlo) \
86 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
87
30ef834a
DL
88/*
89 * generic version. used on NetBSD, Solaris and OSX. really shitty.
440d5faa 90 */
30ef834a 91#else
440d5faa 92
2a5e6235
DL
93#define TIME_ABS_REALTIME 1
94
440d5faa
DL
95#define wait_init(sqlo) do { \
96 pthread_mutex_init(&sqlo->lock, NULL); \
97 pthread_cond_init(&sqlo->wake, NULL); \
98 } while (0)
99#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
100#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
2a5e6235
DL
101#define wait_time(sqlo, val, time, reltime) \
102 pthread_cond_timedwait(&sqlo->wake, \
103 &sqlo->lock, time);
440d5faa
DL
104#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
105#define wait_poke(sqlo) do { \
106 pthread_mutex_lock(&sqlo->lock); \
107 pthread_cond_broadcast(&sqlo->wake); \
108 pthread_mutex_unlock(&sqlo->lock); \
109 } while (0)
110
111#endif
112
113#ifndef wait_init
114#define wait_init(sqlo) /**/
115#define wait_prep(sqlo) /**/
116#define wait_done(sqlo) /**/
117#endif /* wait_init */
118
119
120void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val)
121{
122 seqlock_val_t cur, cal;
123
124 seqlock_assert_valid(val);
125
126 wait_prep(sqlo);
6046b690
DL
127 cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
128
129 while (cur & SEQLOCK_HELD) {
130 cal = SEQLOCK_VAL(cur) - val - 1;
440d5faa
DL
131 assert(cal < 0x40000000 || cal > 0xc0000000);
132 if (cal < 0x80000000)
133 break;
134
6046b690
DL
135 if ((cur & SEQLOCK_WAITERS)
136 || atomic_compare_exchange_weak_explicit(
137 &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
138 memory_order_relaxed, memory_order_relaxed)) {
139 wait_once(sqlo, cur | SEQLOCK_WAITERS);
140 cur = atomic_load_explicit(&sqlo->pos,
141 memory_order_relaxed);
142 }
143 /* else: we failed to swap in cur because it just changed */
440d5faa
DL
144 }
145 wait_done(sqlo);
146}
147
2a5e6235
DL
148bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val,
149 const struct timespec *abs_monotime_limit)
150{
30ef834a
DL
151/*
152 * ABS_REALTIME - used on NetBSD, Solaris and OSX
153 */
1e20238a 154#ifdef TIME_ABS_REALTIME
2a5e6235
DL
155#define time_arg1 &abs_rt
156#define time_arg2 NULL
157#define time_prep
158 struct timespec curmono, abs_rt;
159
160 clock_gettime(CLOCK_MONOTONIC, &curmono);
161 clock_gettime(CLOCK_REALTIME, &abs_rt);
162
163 abs_rt.tv_nsec += abs_monotime_limit->tv_nsec - curmono.tv_nsec;
164 if (abs_rt.tv_nsec < 0) {
165 abs_rt.tv_sec--;
166 abs_rt.tv_nsec += 1000000000;
167 } else if (abs_rt.tv_nsec >= 1000000000) {
168 abs_rt.tv_sec++;
169 abs_rt.tv_nsec -= 1000000000;
170 }
171 abs_rt.tv_sec += abs_monotime_limit->tv_sec - curmono.tv_sec;
172
30ef834a
DL
173/*
174 * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime)
175 */
1e20238a 176#elif defined(TIME_RELATIVE)
2a5e6235
DL
177 struct timespec reltime;
178
179#define time_arg1 abs_monotime_limit
180#define time_arg2 &reltime
181#define time_prep \
182 clock_gettime(CLOCK_MONOTONIC, &reltime); \
183 reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \
184 reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \
185 if (reltime.tv_nsec < 0) { \
186 reltime.tv_sec--; \
187 reltime.tv_nsec += 1000000000; \
188 }
30ef834a
DL
189/*
190 * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC
191 */
2a5e6235
DL
192#else
193#define time_arg1 abs_monotime_limit
194#define time_arg2 NULL
195#define time_prep
196#endif
197
198 bool ret = true;
199 seqlock_val_t cur, cal;
200
201 seqlock_assert_valid(val);
202
203 wait_prep(sqlo);
204 cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
205
206 while (cur & SEQLOCK_HELD) {
207 cal = SEQLOCK_VAL(cur) - val - 1;
208 assert(cal < 0x40000000 || cal > 0xc0000000);
209 if (cal < 0x80000000)
210 break;
211
212 if ((cur & SEQLOCK_WAITERS)
213 || atomic_compare_exchange_weak_explicit(
214 &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
215 memory_order_relaxed, memory_order_relaxed)) {
216 int rv;
217
218 time_prep
219
220 rv = wait_time(sqlo, cur | SEQLOCK_WAITERS, time_arg1,
221 time_arg2);
222 if (rv) {
223 ret = false;
224 break;
225 }
226 cur = atomic_load_explicit(&sqlo->pos,
227 memory_order_relaxed);
228 }
229 }
230 wait_done(sqlo);
231
232 return ret;
233}
234
440d5faa
DL
235bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
236{
237 seqlock_val_t cur;
238
239 seqlock_assert_valid(val);
240
6046b690
DL
241 cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
242 if (!(cur & SEQLOCK_HELD))
f06c4576 243 return true;
6046b690 244 cur = SEQLOCK_VAL(cur) - val - 1;
440d5faa
DL
245 assert(cur < 0x40000000 || cur > 0xc0000000);
246 return cur < 0x80000000;
247}
248
249void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val)
250{
6046b690
DL
251 seqlock_val_t prev;
252
440d5faa
DL
253 seqlock_assert_valid(val);
254
6046b690
DL
255 prev = atomic_exchange_explicit(&sqlo->pos, val, memory_order_relaxed);
256 if (prev & SEQLOCK_WAITERS)
257 wait_poke(sqlo);
440d5faa
DL
258}
259
260void seqlock_release(struct seqlock *sqlo)
261{
6046b690
DL
262 seqlock_val_t prev;
263
264 prev = atomic_exchange_explicit(&sqlo->pos, 0, memory_order_relaxed);
265 if (prev & SEQLOCK_WAITERS)
266 wait_poke(sqlo);
440d5faa
DL
267}
268
269void seqlock_init(struct seqlock *sqlo)
270{
271 sqlo->pos = 0;
272 wait_init(sqlo);
273}
274
275
276seqlock_val_t seqlock_cur(struct seqlock *sqlo)
277{
6046b690
DL
278 return SEQLOCK_VAL(atomic_load_explicit(&sqlo->pos,
279 memory_order_relaxed));
440d5faa
DL
280}
281
282seqlock_val_t seqlock_bump(struct seqlock *sqlo)
283{
6046b690
DL
284 seqlock_val_t val, cur;
285
286 cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
287 seqlock_assert_valid(cur);
288
289 do {
290 val = SEQLOCK_VAL(cur) + SEQLOCK_INCR;
291 } while (!atomic_compare_exchange_weak_explicit(&sqlo->pos, &cur, val,
292 memory_order_relaxed, memory_order_relaxed));
440d5faa 293
6046b690
DL
294 if (cur & SEQLOCK_WAITERS)
295 wait_poke(sqlo);
440d5faa
DL
296 return val;
297}