]>
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 #ifdef HAVE_SYNC_LINUX_FUTEX
40 /* Linux-specific - sys_futex() */
41 #include <sys/syscall.h>
42 #include <linux/futex.h>
44 static long sys_futex(void *addr1
, int op
, int val1
,
45 const struct timespec
*timeout
, void *addr2
, int val3
)
47 return syscall(SYS_futex
, addr1
, op
, val1
, timeout
, addr2
, val3
);
50 #define wait_once(sqlo, val) \
51 sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
52 #define wait_time(sqlo, val, time, reltime) \
53 sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
55 #define wait_poke(sqlo) \
56 sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
58 #elif defined(HAVE_SYNC_OPENBSD_FUTEX)
59 /* OpenBSD variant of the above. */
60 #include <sys/syscall.h>
61 #include <sys/futex.h>
63 #define TIME_RELATIVE 1
65 #define wait_once(sqlo, val) \
66 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
67 #define wait_time(sqlo, val, time, reltime) \
68 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
69 #define wait_poke(sqlo) \
70 futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
72 #elif defined(HAVE_SYNC_UMTX_OP)
73 /* FreeBSD-specific: umtx_op() */
76 #define wait_once(sqlo, val) \
77 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
78 static int wait_time(struct seqlock
*sqlo
, uint32_t val
,
79 const struct timespec
*abstime
,
80 const struct timespec
*reltime
)
83 t
._flags
= UMTX_ABSTIME
;
84 t
._clockid
= CLOCK_MONOTONIC
;
85 memcpy(&t
._timeout
, abstime
, sizeof(t
._timeout
));
86 return _umtx_op((void *)&sqlo
->pos
, UMTX_OP_WAIT_UINT
, val
,
87 (void *)(uintptr_t) sizeof(t
), &t
);
89 #define wait_poke(sqlo) \
90 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
93 /* generic version. used on *BSD, Solaris and OSX.
96 #define TIME_ABS_REALTIME 1
98 #define wait_init(sqlo) do { \
99 pthread_mutex_init(&sqlo->lock, NULL); \
100 pthread_cond_init(&sqlo->wake, NULL); \
102 #define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
103 #define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
104 #define wait_time(sqlo, val, time, reltime) \
105 pthread_cond_timedwait(&sqlo->wake, \
107 #define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
108 #define wait_poke(sqlo) do { \
109 pthread_mutex_lock(&sqlo->lock); \
110 pthread_cond_broadcast(&sqlo->wake); \
111 pthread_mutex_unlock(&sqlo->lock); \
117 #define wait_init(sqlo) /**/
118 #define wait_prep(sqlo) /**/
119 #define wait_done(sqlo) /**/
120 #endif /* wait_init */
123 void seqlock_wait(struct seqlock
*sqlo
, seqlock_val_t val
)
125 seqlock_val_t cur
, cal
;
127 seqlock_assert_valid(val
);
130 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
132 while (cur
& SEQLOCK_HELD
) {
133 cal
= SEQLOCK_VAL(cur
) - val
- 1;
134 assert(cal
< 0x40000000 || cal
> 0xc0000000);
135 if (cal
< 0x80000000)
138 if ((cur
& SEQLOCK_WAITERS
)
139 || atomic_compare_exchange_weak_explicit(
140 &sqlo
->pos
, &cur
, cur
| SEQLOCK_WAITERS
,
141 memory_order_relaxed
, memory_order_relaxed
)) {
142 wait_once(sqlo
, cur
| SEQLOCK_WAITERS
);
143 cur
= atomic_load_explicit(&sqlo
->pos
,
144 memory_order_relaxed
);
146 /* else: we failed to swap in cur because it just changed */
151 bool seqlock_timedwait(struct seqlock
*sqlo
, seqlock_val_t val
,
152 const struct timespec
*abs_monotime_limit
)
154 #if 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 struct timespec reltime
;
176 #define time_arg1 abs_monotime_limit
177 #define time_arg2 &reltime
179 clock_gettime(CLOCK_MONOTONIC, &reltime); \
180 reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \
181 reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \
182 if (reltime.tv_nsec < 0) { \
184 reltime.tv_nsec += 1000000000; \
187 #define time_arg1 abs_monotime_limit
188 #define time_arg2 NULL
193 seqlock_val_t cur
, cal
;
195 seqlock_assert_valid(val
);
198 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
200 while (cur
& SEQLOCK_HELD
) {
201 cal
= SEQLOCK_VAL(cur
) - val
- 1;
202 assert(cal
< 0x40000000 || cal
> 0xc0000000);
203 if (cal
< 0x80000000)
206 if ((cur
& SEQLOCK_WAITERS
)
207 || atomic_compare_exchange_weak_explicit(
208 &sqlo
->pos
, &cur
, cur
| SEQLOCK_WAITERS
,
209 memory_order_relaxed
, memory_order_relaxed
)) {
214 rv
= wait_time(sqlo
, cur
| SEQLOCK_WAITERS
, time_arg1
,
220 cur
= atomic_load_explicit(&sqlo
->pos
,
221 memory_order_relaxed
);
229 bool seqlock_check(struct seqlock
*sqlo
, seqlock_val_t val
)
233 seqlock_assert_valid(val
);
235 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
236 if (!(cur
& SEQLOCK_HELD
))
238 cur
= SEQLOCK_VAL(cur
) - val
- 1;
239 assert(cur
< 0x40000000 || cur
> 0xc0000000);
240 return cur
< 0x80000000;
243 void seqlock_acquire_val(struct seqlock
*sqlo
, seqlock_val_t val
)
247 seqlock_assert_valid(val
);
249 prev
= atomic_exchange_explicit(&sqlo
->pos
, val
, memory_order_relaxed
);
250 if (prev
& SEQLOCK_WAITERS
)
254 void seqlock_release(struct seqlock
*sqlo
)
258 prev
= atomic_exchange_explicit(&sqlo
->pos
, 0, memory_order_relaxed
);
259 if (prev
& SEQLOCK_WAITERS
)
263 void seqlock_init(struct seqlock
*sqlo
)
270 seqlock_val_t
seqlock_cur(struct seqlock
*sqlo
)
272 return SEQLOCK_VAL(atomic_load_explicit(&sqlo
->pos
,
273 memory_order_relaxed
));
276 seqlock_val_t
seqlock_bump(struct seqlock
*sqlo
)
278 seqlock_val_t val
, cur
;
280 cur
= atomic_load_explicit(&sqlo
->pos
, memory_order_relaxed
);
281 seqlock_assert_valid(cur
);
284 val
= SEQLOCK_VAL(cur
) + SEQLOCK_INCR
;
285 } while (!atomic_compare_exchange_weak_explicit(&sqlo
->pos
, &cur
, val
,
286 memory_order_relaxed
, memory_order_relaxed
));
288 if (cur
& SEQLOCK_WAITERS
)