f1181ee4a6011022af997b53b375addc93ba9137
[userspace-rcu.git] / include / urcu / futex.h
1 // SPDX-FileCopyrightText: 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-or-later
4
5 #ifndef _URCU_FUTEX_H
6 #define _URCU_FUTEX_H
7
8 /*
9 * Userspace RCU - sys_futex/compat_futex header.
10 */
11
12 #include <urcu/config.h>
13 #include <urcu/syscall-compat.h>
14
15 #include <errno.h>
16 #include <stdint.h>
17 #include <time.h>
18
19 #if (defined(__linux__) && defined(__NR_futex))
20
21 /* For backwards compat */
22 # define CONFIG_RCU_HAVE_FUTEX 1
23
24 # include <unistd.h>
25 # include <errno.h>
26 # include <urcu/compiler.h>
27 # include <urcu/arch.h>
28 # include <urcu/assert.h>
29
30 #elif defined(__FreeBSD__)
31
32 # include <sys/types.h>
33 # include <sys/umtx.h>
34
35 #elif defined(__OpenBSD__)
36
37 # include <sys/time.h>
38 # include <sys/futex.h>
39
40 #endif
41
42 #ifdef __cplusplus
43 extern "C" {
44 #endif
45
46 #ifndef __OpenBSD__
47 # define FUTEX_WAIT 0
48 # define FUTEX_WAKE 1
49 #endif
50
51 /*
52 * sys_futex compatibility header.
53 * Use *only* *either of* futex_noasync OR futex_async on a given address.
54 *
55 * futex_noasync cannot be executed in signal handlers, but ensures that
56 * it will be put in a wait queue even in compatibility mode.
57 *
58 * futex_async is signal-handler safe for the wakeup. It uses polling
59 * on the wait-side in compatibility mode.
60 *
61 * BEWARE: sys_futex() FUTEX_WAIT may return early if interrupted
62 * (returns EINTR).
63 */
64
65 extern int compat_futex_noasync(int32_t *uaddr, int op, int32_t val,
66 const struct timespec *timeout, int32_t *uaddr2, int32_t val3);
67 extern int compat_futex_async(int32_t *uaddr, int op, int32_t val,
68 const struct timespec *timeout, int32_t *uaddr2, int32_t val3);
69
70 #if (defined(__linux__) && defined(__NR_futex))
71
72 static inline int futex(int32_t *uaddr, int op, int32_t val,
73 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
74 {
75 return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
76 }
77
78 static inline int futex_noasync(int32_t *uaddr, int op, int32_t val,
79 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
80 {
81 int ret;
82
83 ret = futex(uaddr, op, val, timeout, uaddr2, val3);
84 if (caa_unlikely(ret < 0 && errno == ENOSYS)) {
85 /*
86 * The fallback on ENOSYS is the async-safe version of
87 * the compat futex implementation, because the
88 * async-safe compat implementation allows being used
89 * concurrently with calls to futex(). Indeed, sys_futex
90 * FUTEX_WAIT, on some architectures (mips and parisc),
91 * within a given process, spuriously return ENOSYS due
92 * to signal restart bugs on some kernel versions.
93 */
94 return compat_futex_async(uaddr, op, val, timeout,
95 uaddr2, val3);
96 }
97 return ret;
98
99 }
100
101 static inline int futex_async(int32_t *uaddr, int op, int32_t val,
102 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
103 {
104 int ret;
105
106 ret = futex(uaddr, op, val, timeout, uaddr2, val3);
107 if (caa_unlikely(ret < 0 && errno == ENOSYS)) {
108 return compat_futex_async(uaddr, op, val, timeout,
109 uaddr2, val3);
110 }
111 return ret;
112 }
113
114 #elif defined(__FreeBSD__)
115
116 static inline int futex_async(int32_t *uaddr, int op, int32_t val,
117 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
118 {
119 int umtx_op;
120 void *umtx_uaddr = NULL, *umtx_uaddr2 = NULL;
121 struct _umtx_time umtx_timeout = {
122 ._flags = UMTX_ABSTIME,
123 ._clockid = CLOCK_MONOTONIC,
124 };
125
126 /*
127 * Check if NULL or zero. Don't let users expect that they are
128 * taken into account.
129 */
130 urcu_posix_assert(!uaddr2);
131 urcu_posix_assert(!val3);
132
133 switch (op) {
134 case FUTEX_WAIT:
135 /* On FreeBSD, a "u_int" is a 32-bit integer. */
136 umtx_op = UMTX_OP_WAIT_UINT;
137 if (timeout != NULL) {
138 umtx_timeout._timeout = *timeout;
139 umtx_uaddr = (void *) sizeof(umtx_timeout);
140 umtx_uaddr2 = (void *) &umtx_timeout;
141 }
142 break;
143 case FUTEX_WAKE:
144 umtx_op = UMTX_OP_WAKE;
145 break;
146 default:
147 errno = EINVAL;
148 return -1;
149 }
150
151 return _umtx_op(uaddr, umtx_op, (uint32_t) val, umtx_uaddr,
152 umtx_uaddr2);
153 }
154
155 static inline int futex_noasync(int32_t *uaddr, int op, int32_t val,
156 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
157 {
158 return futex_async(uaddr, op, val, timeout, uaddr2, val3);
159 }
160
161 #elif defined(__OpenBSD__)
162
163 static inline int futex_noasync(int32_t *uaddr, int op, int32_t val,
164 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
165 {
166 int ret;
167
168 /*
169 * Check that val3 is zero. Don't let users expect that it is
170 * taken into account.
171 */
172 urcu_posix_assert(!val3);
173
174 ret = futex((volatile uint32_t *) uaddr, op, val, timeout,
175 (volatile uint32_t *) uaddr2);
176 if (caa_unlikely(ret < 0 && errno == ENOSYS)) {
177 return compat_futex_noasync(uaddr, op, val, timeout,
178 uaddr2, val3);
179 }
180 return ret;
181 }
182
183 static inline int futex_async(int32_t *uaddr, int op, int32_t val,
184 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
185 {
186 int ret;
187
188 /*
189 * Check that val3 is zero. Don't let users expect that it is
190 * taken into account.
191 */
192 urcu_posix_assert(!val3);
193
194 ret = futex((volatile uint32_t *) uaddr, op, val, timeout,
195 (volatile uint32_t *) uaddr2);
196 if (caa_unlikely(ret < 0 && errno == ENOSYS)) {
197 return compat_futex_async(uaddr, op, val, timeout,
198 uaddr2, val3);
199 }
200 return ret;
201 }
202
203 #elif defined(__CYGWIN__)
204
205 /*
206 * The futex_noasync compat code uses a weak symbol to share state across
207 * different shared object which is not possible on Windows with the
208 * Portable Executable format. Use the async compat code for both cases.
209 */
210 static inline int futex_noasync(int32_t *uaddr, int op, int32_t val,
211 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
212 {
213 return compat_futex_async(uaddr, op, val, timeout, uaddr2, val3);
214 }
215
216 static inline int futex_async(int32_t *uaddr, int op, int32_t val,
217 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
218 {
219 return compat_futex_async(uaddr, op, val, timeout, uaddr2, val3);
220 }
221
222 #else
223
224 static inline int futex_noasync(int32_t *uaddr, int op, int32_t val,
225 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
226 {
227 return compat_futex_noasync(uaddr, op, val, timeout, uaddr2, val3);
228 }
229
230 static inline int futex_async(int32_t *uaddr, int op, int32_t val,
231 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
232 {
233 return compat_futex_async(uaddr, op, val, timeout, uaddr2, val3);
234 }
235
236 #endif
237
238 #ifdef __cplusplus
239 }
240 #endif
241
242 #endif /* _URCU_FUTEX_H */
This page took 0.041446 seconds and 5 git commands to generate.