Go to the documentation of this file.
21 #if !defined(P99_FUTEX_LINUX_H) && !defined(P00_DOXYGEN)
22 #define P99_FUTEX_LINUX_H
26 #if !defined(ATOMIC_VAR_INIT) && defined(__GNUC__)
37 #if (defined(__linux__) && !defined(NO_FUTEX)) || defined(DOXYGEN)
38 #define P00_FUTEX_LINUX 1
41 # include <linux/futex.h>
49 # define FUTEX_REQUEUE 3
50 # define FUTEX_CMP_REQUEUE 4
53 # include <sys/syscall.h>
55 long syscall(
long number, ...);
73 int p00_futex(
int *uaddr,
84 return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
87 #define p00_futex(...) P99_CALL_DEFARG(p00_futex, 6, __VA_ARGS__)
88 #define p00_futex_defarg_3() ((void*)0)
89 #define p00_futex_defarg_4() ((void*)0)
90 #define p00_futex_defarg_5() 0
114 int p00_futex_wait_once(
int* uaddr,
int val) {
115 int ret = p00_futex(uaddr, FUTEX_WAIT, val);
173 #define P00_FUTEX_WAIT(ADDR, NAME, EXPECTED) \
175 register int volatile*const p = (int volatile*)(ADDR); \
177 register int NAME = *p; \
178 if (P99_LIKELY(EXPECTED)) break; \
179 register int ret = p00_futex_wait_once((int*)p, NAME); \
180 if (P99_UNLIKELY(ret)) { \
197 int p00_futex_wake(
int* uaddr,
int wakeup) {
198 int ret = p00_futex(uaddr, FUTEX_WAKE, wakeup);
209 int p00_futex_signal(
int* uaddr) {
210 return p00_futex_wake(uaddr, 1);
221 int p00_futex_broadcast(
int* uaddr) {
222 return p00_futex_wake(uaddr, INT_MAX);
225 # ifndef P99_FUTEX_INITIALIZER
226 # define P99_FUTEX_INITIALIZER(INITIAL) ATOMIC_VAR_INIT(INITIAL)
232 atomic_init(p00_c, p00_ini);
240 atomic_store(p00_c, UINT_MAX);
246 return atomic_load(p00_fut);
251 unsigned p00_wmin,
unsigned p00_wmax) {
252 if (p00_wmax < p00_wmin) p00_wmax = p00_wmin;
254 unsigned volatile*
const p00_cnt = (
unsigned*)p00_cntp;
256 "linux futex supposes that there is no hidden lock field");
258 register signed p00_wok = p00_futex_wake((
int*)p00_cnt, p00_wmax);
259 assert(p00_wok >= 0);
260 if (p00_wok >= p00_wmin)
break;
269 unsigned volatile*
const p00_cnt = (
unsigned*)p00_cntp;
271 "linux futex supposes that there is no hidden lock field");
273 unsigned p00_act = *p00_cnt;
274 register int p00_ret = p00_futex_wait_once((
int*)p00_cnt, p00_act);
276 default: assert(!p00_ret);
288 unsigned p00_cstart,
unsigned p00_clen,
289 unsigned p00_wmin,
unsigned p00_wmax) {
290 unsigned p00_act = atomic_fetch_add(futex, p00_hmuch);
291 register unsigned const ret = p00_act + p00_hmuch;
292 if (p00_clen &&
P99_IN_RANGE(ret, p00_cstart, p00_clen))
299 unsigned p00_cstart,
unsigned p00_clen,
300 unsigned p00_wmin,
unsigned p00_wmax) {
301 unsigned p00_act = atomic_exchange(futex, p00_desired);
302 if (p00_clen &&
P99_IN_RANGE(p00_desired, p00_cstart, p00_clen))
307 #ifndef P99_FUTEX_COMPARE_EXCHANGE
309 # define P99_FUTEX_COMPARE_EXCHANGE(FUTEX, ACT, EXPECTED, DESIRED, WAKEMIN, WAKEMAX) \
311 _Atomic(unsigned) volatile*const p00Mcntp = (FUTEX); \
312 unsigned volatile*const p00Mcnt = (unsigned*)p00Mcntp; \
313 static_assert(sizeof *p00Mcntp == sizeof *p00Mcnt, \
314 "linux futex stuff supposes that there is no hidden lock field"); \
315 unsigned p00Mact = *p00Mcnt; \
317 register unsigned const ACT = p00Mact; \
318 if (P99_LIKELY(EXPECTED)) { \
319 register unsigned const p00Mdes = (DESIRED); \
322 if (ACT == p00Mdes) break; \
323 if (atomic_compare_exchange_weak(p00Mcntp, &p00Mact, p00Mdes)) { \
324 register unsigned p00Mwmin = (WAKEMIN); \
325 register unsigned p00Mwmax = (WAKEMAX); \
326 p99_futex_wakeup(p00Mcntp, p00Mwmin, p00Mwmax); \
330 register int p00Mret = p00_futex_wait_once((int*)p00Mcnt, ACT); \
332 default: assert(!p00Mret); \
335 case EWOULDBLOCK: ; \
338 p00Mact = *p00Mcnt; \
void p99_futex_destroy(p99_futex *p00_c)
Destroy an p99_futex object.
A counter similar to a conditional variable that allows atomic increment and decrement and to wait fo...
#define static_assert(EXPR, DIAGSTR)
Evaluate expression EXPR at compile time and ensure that it is fulfilled.
#define P99_IN_RANGE(R, S, L)
check if R is in the range [S, S + L)
unsigned p99_futex_exchange(p99_futex volatile *p00_fut, unsigned p00_desired, unsigned p00_cstart, unsigned p00_clen, unsigned p00_wmin, unsigned p00_wmax)
Unconditionally and atomically set the futex p00_fut to value p00_desired.
#define p99_inline
Try to force a function always to be inlined.
#define P99_FUTEX_COMPARE_EXCHANGE(FUTEX, ACT, EXPECTED, DESIRED, WAKEMIN, WAKEMAX)
a catch all macro to operate on p99_futex
#define P99_DEFARG_DOCU(NAME)
Provide a documentation section to a function defined with P99_CALL_DEFARG.
unsigned p99_futex_load(p99_futex volatile *p00_fut)
Obtain the value of futex p00_fut atomically.
void p99_futex_wakeup(p99_futex volatile *p00_fut, unsigned p00_wmin, unsigned p00_wmax)
Wake up threads that are waiting for a futex.
void p99_futex_wait(p99_futex volatile *p00_fut)
Unconditionally wait for futex p00_fut.
unsigned p99_futex_add(p99_futex volatile *p00_fut, unsigned p00_hmuch, unsigned p00_cstart, unsigned p00_clen, unsigned p00_wmin, unsigned p00_wmax)
increment the counter of p00_fut atomically by p00_hmuch.
#define P99_UNLIKELY(...)
Mark the conditional expression as being unlikely.
p99_futex * p99_futex_init(p99_futex *p00_c, unsigned p00_ini)
Initialize an p99_futex object.