]> git.proxmox.com Git - mirror_frr.git/blame - lib/seqlock.c
lib: typesafe rb-tree
[mirror_frr.git] / lib / seqlock.c
CommitLineData
440d5faa
DL
1/*
2 * "Sequence" lock primitive
3 *
4 * Copyright (C) 2015 David Lamparter <equinox@diac24.net>
5 *
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.
10 *
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.
15 *
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
20 */
21
22#define _GNU_SOURCE
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include <unistd.h>
29#include <limits.h>
30#include <errno.h>
31#include <sys/types.h>
32#include <sys/time.h>
33#include <pthread.h>
34#include <assert.h>
35
36#include "seqlock.h"
37
38#ifdef HAVE_SYNC_LINUX_FUTEX
39/* Linux-specific - sys_futex() */
40#include <sys/syscall.h>
41#include <linux/futex.h>
42
43static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
44 void *addr2, int val3)
45{
46 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
47}
48
49#define wait_once(sqlo, val) \
50 sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
51#define wait_poke(sqlo) \
52 sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
53
54#elif defined(HAVE_SYNC_OPENBSD_FUTEX)
55/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */
56#include <sys/syscall.h>
57#include <sys/futex.h>
58
59#define wait_once(sqlo, val) \
60 futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
61#define wait_poke(sqlo) \
62 futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
63
64#elif defined(HAVE_SYNC_UMTX_OP)
65/* FreeBSD-specific: umtx_op() */
66#include <sys/umtx.h>
67
68#define wait_once(sqlo, val) \
69 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
70#define wait_poke(sqlo) \
71 _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
72
73#else
74/* generic version. used on *BSD, Solaris and OSX.
75 */
76
77#define wait_init(sqlo) do { \
78 pthread_mutex_init(&sqlo->lock, NULL); \
79 pthread_cond_init(&sqlo->wake, NULL); \
80 } while (0)
81#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
82#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
83#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
84#define wait_poke(sqlo) do { \
85 pthread_mutex_lock(&sqlo->lock); \
86 pthread_cond_broadcast(&sqlo->wake); \
87 pthread_mutex_unlock(&sqlo->lock); \
88 } while (0)
89
90#endif
91
92#ifndef wait_init
93#define wait_init(sqlo) /**/
94#define wait_prep(sqlo) /**/
95#define wait_done(sqlo) /**/
96#endif /* wait_init */
97
98
99void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val)
100{
101 seqlock_val_t cur, cal;
102
103 seqlock_assert_valid(val);
104
105 wait_prep(sqlo);
106 while (1) {
107 cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
108 if (!(cur & 1))
109 break;
110 cal = cur - val - 1;
111 assert(cal < 0x40000000 || cal > 0xc0000000);
112 if (cal < 0x80000000)
113 break;
114
115 wait_once(sqlo, cur);
116 }
117 wait_done(sqlo);
118}
119
120bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
121{
122 seqlock_val_t cur;
123
124 seqlock_assert_valid(val);
125
126 cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
127 if (!(cur & 1))
128 return 1;
129 cur -= val;
130 assert(cur < 0x40000000 || cur > 0xc0000000);
131 return cur < 0x80000000;
132}
133
134void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val)
135{
136 seqlock_assert_valid(val);
137
138 atomic_store_explicit(&sqlo->pos, val, memory_order_release);
139 wait_poke(sqlo);
140}
141
142void seqlock_release(struct seqlock *sqlo)
143{
144 atomic_store_explicit(&sqlo->pos, 0, memory_order_release);
145 wait_poke(sqlo);
146}
147
148void seqlock_init(struct seqlock *sqlo)
149{
150 sqlo->pos = 0;
151 wait_init(sqlo);
152}
153
154
155seqlock_val_t seqlock_cur(struct seqlock *sqlo)
156{
157 return atomic_load_explicit(&sqlo->pos, memory_order_acquire);
158}
159
160seqlock_val_t seqlock_bump(struct seqlock *sqlo)
161{
162 seqlock_val_t val;
163
164 val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release);
165 wait_poke(sqlo);
166 return val;
167}