]>
git.proxmox.com Git - mirror_frr.git/blob - lib/seqlock.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * "Sequence" lock primitive
5 * Copyright (C) 2015 David Lamparter <equinox@diac24.net>
18 #include <sys/types.h>
25 /****************************************
26 * OS specific synchronization wrappers *
27 ****************************************/
32 #ifdef HAVE_SYNC_LINUX_FUTEX
33 #include <sys/syscall.h>
34 #include <linux/futex.h>
36 static long sys_futex(void *addr1
, int op
, int val1
,
37 const struct timespec
*timeout
, void *addr2
, int val3
)
39 return syscall(SYS_futex
, addr1
, op
, val1
, timeout
, addr2
, val3
);
42 #define wait_once(sqlo, val) \
43 sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
44 #define wait_time(sqlo, val, time, reltime) \
45 sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
47 #define wait_poke(sqlo) \
48 sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
51 * OpenBSD: sys_futex(), almost the same as on Linux
53 #elif defined(HAVE_SYNC_OPENBSD_FUTEX)
54 #include <sys/syscall.h>
55 #include <sys/futex.h>
57 #define TIME_RELATIVE 1
59 #define wait_once(sqlo, val) \
60 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
61 #define wait_time(sqlo, val, time, reltime) \
62 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
63 #define wait_poke(sqlo) \
64 futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
69 #elif defined(HAVE_SYNC_UMTX_OP)
72 #define wait_once(sqlo, val) \
73 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
74 static int wait_time(struct seqlock
*sqlo
, uint32_t val
,
75 const struct timespec
*abstime
,
76 const struct timespec
*reltime
)
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
);
85 #define wait_poke(sqlo) \
86 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
89 * generic version. used on NetBSD, Solaris and OSX. really shitty.
93 #define TIME_ABS_REALTIME 1
95 #define wait_init(sqlo) do { \
96 pthread_mutex_init(&sqlo->lock, NULL); \
97 pthread_cond_init(&sqlo->wake, NULL); \
99 #define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
100 #define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
101 #define wait_time(sqlo, val, time, reltime) \
102 pthread_cond_timedwait(&sqlo->wake, \
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); \
114 #define wait_init(sqlo) /**/
115 #define wait_prep(sqlo) /**/
116 #define wait_done(sqlo) /**/
117 #endif /* wait_init */
120 void seqlock_wait(struct seqlock
*sqlo
, seqlock_val_t val
)
122 seqlock_val_t cur
, cal
;
124 seqlock_assert_valid(val
);
127 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
129 while (cur
& SEQLOCK_HELD
) {
130 cal
= SEQLOCK_VAL(cur
) - val
- 1;
131 assert(cal
< 0x40000000 || cal
> 0xc0000000);
132 if (cal
< 0x80000000)
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
);
143 /* else: we failed to swap in cur because it just changed */
148 bool seqlock_timedwait(struct seqlock
*sqlo
, seqlock_val_t val
,
149 const struct timespec
*abs_monotime_limit
)
152 * ABS_REALTIME - used on NetBSD, Solaris and OSX
154 #ifdef TIME_ABS_REALTIME
155 #define time_arg1 &abs_rt
156 #define time_arg2 NULL
158 struct timespec curmono
, abs_rt
;
160 clock_gettime(CLOCK_MONOTONIC
, &curmono
);
161 clock_gettime(CLOCK_REALTIME
, &abs_rt
);
163 abs_rt
.tv_nsec
+= abs_monotime_limit
->tv_nsec
- curmono
.tv_nsec
;
164 if (abs_rt
.tv_nsec
< 0) {
166 abs_rt
.tv_nsec
+= 1000000000;
167 } else if (abs_rt
.tv_nsec
>= 1000000000) {
169 abs_rt
.tv_nsec
-= 1000000000;
171 abs_rt
.tv_sec
+= abs_monotime_limit
->tv_sec
- curmono
.tv_sec
;
174 * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime)
176 #elif defined(TIME_RELATIVE)
177 struct timespec reltime
;
179 #define time_arg1 abs_monotime_limit
180 #define time_arg2 &reltime
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) { \
187 reltime.tv_nsec += 1000000000; \
190 * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC
193 #define time_arg1 abs_monotime_limit
194 #define time_arg2 NULL
199 seqlock_val_t cur
, cal
;
201 seqlock_assert_valid(val
);
204 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
206 while (cur
& SEQLOCK_HELD
) {
207 cal
= SEQLOCK_VAL(cur
) - val
- 1;
208 assert(cal
< 0x40000000 || cal
> 0xc0000000);
209 if (cal
< 0x80000000)
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
)) {
220 rv
= wait_time(sqlo
, cur
| SEQLOCK_WAITERS
, time_arg1
,
226 cur
= atomic_load_explicit(&sqlo
->pos
,
227 memory_order_relaxed
);
235 bool seqlock_check(struct seqlock
*sqlo
, seqlock_val_t val
)
239 seqlock_assert_valid(val
);
241 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
242 if (!(cur
& SEQLOCK_HELD
))
244 cur
= SEQLOCK_VAL(cur
) - val
- 1;
245 assert(cur
< 0x40000000 || cur
> 0xc0000000);
246 return cur
< 0x80000000;
249 void seqlock_acquire_val(struct seqlock
*sqlo
, seqlock_val_t val
)
253 seqlock_assert_valid(val
);
255 prev
= atomic_exchange_explicit(&sqlo
->pos
, val
, memory_order_relaxed
);
256 if (prev
& SEQLOCK_WAITERS
)
260 void seqlock_release(struct seqlock
*sqlo
)
264 prev
= atomic_exchange_explicit(&sqlo
->pos
, 0, memory_order_relaxed
);
265 if (prev
& SEQLOCK_WAITERS
)
269 void seqlock_init(struct seqlock
*sqlo
)
276 seqlock_val_t
seqlock_cur(struct seqlock
*sqlo
)
278 return SEQLOCK_VAL(atomic_load_explicit(&sqlo
->pos
,
279 memory_order_relaxed
));
282 seqlock_val_t
seqlock_bump(struct seqlock
*sqlo
)
284 seqlock_val_t val
, cur
;
286 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
287 seqlock_assert_valid(cur
);
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
));
294 if (cur
& SEQLOCK_WAITERS
)