]>
git.proxmox.com Git - mirror_frr.git/blob - lib/seqlock.c
2 * "Sequence" lock primitive
4 * Copyright (C) 2015 David Lamparter <equinox@diac24.net>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
32 #include <sys/types.h>
39 /****************************************
40 * OS specific synchronization wrappers *
41 ****************************************/
46 #ifdef HAVE_SYNC_LINUX_FUTEX
47 #include <sys/syscall.h>
48 #include <linux/futex.h>
50 static long sys_futex(void *addr1
, int op
, int val1
,
51 const struct timespec
*timeout
, void *addr2
, int val3
)
53 return syscall(SYS_futex
, addr1
, op
, val1
, timeout
, addr2
, val3
);
56 #define wait_once(sqlo, val) \
57 sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
58 #define wait_time(sqlo, val, time, reltime) \
59 sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
61 #define wait_poke(sqlo) \
62 sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
65 * OpenBSD: sys_futex(), almost the same as on Linux
67 #elif defined(HAVE_SYNC_OPENBSD_FUTEX)
68 #include <sys/syscall.h>
69 #include <sys/futex.h>
71 #define TIME_RELATIVE 1
73 #define wait_once(sqlo, val) \
74 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
75 #define wait_time(sqlo, val, time, reltime) \
76 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
77 #define wait_poke(sqlo) \
78 futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
83 #elif defined(HAVE_SYNC_UMTX_OP)
86 #define wait_once(sqlo, val) \
87 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
88 static int wait_time(struct seqlock
*sqlo
, uint32_t val
,
89 const struct timespec
*abstime
,
90 const struct timespec
*reltime
)
93 t
._flags
= UMTX_ABSTIME
;
94 t
._clockid
= CLOCK_MONOTONIC
;
95 memcpy(&t
._timeout
, abstime
, sizeof(t
._timeout
));
96 return _umtx_op((void *)&sqlo
->pos
, UMTX_OP_WAIT_UINT
, val
,
97 (void *)(uintptr_t) sizeof(t
), &t
);
99 #define wait_poke(sqlo) \
100 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
103 * generic version. used on NetBSD, Solaris and OSX. really shitty.
107 #define TIME_ABS_REALTIME 1
109 #define wait_init(sqlo) do { \
110 pthread_mutex_init(&sqlo->lock, NULL); \
111 pthread_cond_init(&sqlo->wake, NULL); \
113 #define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
114 #define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
115 #define wait_time(sqlo, val, time, reltime) \
116 pthread_cond_timedwait(&sqlo->wake, \
118 #define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
119 #define wait_poke(sqlo) do { \
120 pthread_mutex_lock(&sqlo->lock); \
121 pthread_cond_broadcast(&sqlo->wake); \
122 pthread_mutex_unlock(&sqlo->lock); \
128 #define wait_init(sqlo) /**/
129 #define wait_prep(sqlo) /**/
130 #define wait_done(sqlo) /**/
131 #endif /* wait_init */
134 void seqlock_wait(struct seqlock
*sqlo
, seqlock_val_t val
)
136 seqlock_val_t cur
, cal
;
138 seqlock_assert_valid(val
);
141 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
143 while (cur
& SEQLOCK_HELD
) {
144 cal
= SEQLOCK_VAL(cur
) - val
- 1;
145 assert(cal
< 0x40000000 || cal
> 0xc0000000);
146 if (cal
< 0x80000000)
149 if ((cur
& SEQLOCK_WAITERS
)
150 || atomic_compare_exchange_weak_explicit(
151 &sqlo
->pos
, &cur
, cur
| SEQLOCK_WAITERS
,
152 memory_order_relaxed
, memory_order_relaxed
)) {
153 wait_once(sqlo
, cur
| SEQLOCK_WAITERS
);
154 cur
= atomic_load_explicit(&sqlo
->pos
,
155 memory_order_relaxed
);
157 /* else: we failed to swap in cur because it just changed */
162 bool seqlock_timedwait(struct seqlock
*sqlo
, seqlock_val_t val
,
163 const struct timespec
*abs_monotime_limit
)
166 * ABS_REALTIME - used on NetBSD, Solaris and OSX
168 #ifdef TIME_ABS_REALTIME
169 #define time_arg1 &abs_rt
170 #define time_arg2 NULL
172 struct timespec curmono
, abs_rt
;
174 clock_gettime(CLOCK_MONOTONIC
, &curmono
);
175 clock_gettime(CLOCK_REALTIME
, &abs_rt
);
177 abs_rt
.tv_nsec
+= abs_monotime_limit
->tv_nsec
- curmono
.tv_nsec
;
178 if (abs_rt
.tv_nsec
< 0) {
180 abs_rt
.tv_nsec
+= 1000000000;
181 } else if (abs_rt
.tv_nsec
>= 1000000000) {
183 abs_rt
.tv_nsec
-= 1000000000;
185 abs_rt
.tv_sec
+= abs_monotime_limit
->tv_sec
- curmono
.tv_sec
;
188 * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime)
190 #elif defined(TIME_RELATIVE)
191 struct timespec reltime
;
193 #define time_arg1 abs_monotime_limit
194 #define time_arg2 &reltime
196 clock_gettime(CLOCK_MONOTONIC, &reltime); \
197 reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \
198 reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \
199 if (reltime.tv_nsec < 0) { \
201 reltime.tv_nsec += 1000000000; \
204 * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC
207 #define time_arg1 abs_monotime_limit
208 #define time_arg2 NULL
213 seqlock_val_t cur
, cal
;
215 seqlock_assert_valid(val
);
218 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
220 while (cur
& SEQLOCK_HELD
) {
221 cal
= SEQLOCK_VAL(cur
) - val
- 1;
222 assert(cal
< 0x40000000 || cal
> 0xc0000000);
223 if (cal
< 0x80000000)
226 if ((cur
& SEQLOCK_WAITERS
)
227 || atomic_compare_exchange_weak_explicit(
228 &sqlo
->pos
, &cur
, cur
| SEQLOCK_WAITERS
,
229 memory_order_relaxed
, memory_order_relaxed
)) {
234 rv
= wait_time(sqlo
, cur
| SEQLOCK_WAITERS
, time_arg1
,
240 cur
= atomic_load_explicit(&sqlo
->pos
,
241 memory_order_relaxed
);
249 bool seqlock_check(struct seqlock
*sqlo
, seqlock_val_t val
)
253 seqlock_assert_valid(val
);
255 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
256 if (!(cur
& SEQLOCK_HELD
))
258 cur
= SEQLOCK_VAL(cur
) - val
- 1;
259 assert(cur
< 0x40000000 || cur
> 0xc0000000);
260 return cur
< 0x80000000;
263 void seqlock_acquire_val(struct seqlock
*sqlo
, seqlock_val_t val
)
267 seqlock_assert_valid(val
);
269 prev
= atomic_exchange_explicit(&sqlo
->pos
, val
, memory_order_relaxed
);
270 if (prev
& SEQLOCK_WAITERS
)
274 void seqlock_release(struct seqlock
*sqlo
)
278 prev
= atomic_exchange_explicit(&sqlo
->pos
, 0, memory_order_relaxed
);
279 if (prev
& SEQLOCK_WAITERS
)
283 void seqlock_init(struct seqlock
*sqlo
)
290 seqlock_val_t
seqlock_cur(struct seqlock
*sqlo
)
292 return SEQLOCK_VAL(atomic_load_explicit(&sqlo
->pos
,
293 memory_order_relaxed
));
296 seqlock_val_t
seqlock_bump(struct seqlock
*sqlo
)
298 seqlock_val_t val
, cur
;
300 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
301 seqlock_assert_valid(cur
);
304 val
= SEQLOCK_VAL(cur
) + SEQLOCK_INCR
;
305 } while (!atomic_compare_exchange_weak_explicit(&sqlo
->pos
, &cur
, val
,
306 memory_order_relaxed
, memory_order_relaxed
));
308 if (cur
& SEQLOCK_WAITERS
)