P99
p99_futex_linux.h
Go to the documentation of this file.
1 /* This may look like nonsense, but it really is -*- mode: C; coding: utf-8 -*- */
2 /* */
3 /* Except for parts copied from previous work and as explicitly stated below, */
4 /* the author and copyright holder for this work is */
5 /* (C) copyright 2012-2014 Jens Gustedt, INRIA, France */
6 /* */
7 /* This file is free software; it is part of the P99 project. */
8 /* */
9 /* Licensed under the Apache License, Version 2.0 (the "License"); */
10 /* you may not use this file except in compliance with the License. */
11 /* You may obtain a copy of the License at */
12 /* */
13 /* http://www.apache.org/licenses/LICENSE-2.0 */
14 /* */
15 /* Unless required by applicable law or agreed to in writing, software */
16 /* distributed under the License is distributed on an "AS IS" BASIS, */
17 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
18 /* See the License for the specific language governing permissions and */
19 /* limitations under the License. */
20 /* */
21 #if !defined(P99_FUTEX_LINUX_H) && !defined(P00_DOXYGEN)
22 #define P99_FUTEX_LINUX_H
23 
24 #include "p99_atomic.h"
25 #include "p99_threads.h"
26 #if !defined(ATOMIC_VAR_INIT) && defined(__GNUC__)
27 # include "p99_atomic.h"
28 #endif
29 #include "p99_posix_default.h"
30 
37 #if (defined(__linux__) && !defined(NO_FUTEX)) || defined(DOXYGEN)
38 #define P00_FUTEX_LINUX 1
39 
40 # if __GLIBC__
41 # include <linux/futex.h>
42 # else
43 /* Other C libraries (e.g musl) do not provide values for futex
44  operations. Only the first five operations are documented, so
45  probably the occasional user of this will not expect more. */
46 # define FUTEX_WAIT 0
47 # define FUTEX_WAKE 1
48 # define FUTEX_FD 2
49 # define FUTEX_REQUEUE 3
50 # define FUTEX_CMP_REQUEUE 4
51 # endif
52 # include <unistd.h>
53 # include <sys/syscall.h>
54 
55 long syscall(long number, ...);
56 
71 P99_DEFARG_DOCU(p00_futex)
73 int p00_futex(int *uaddr,
74  int op,
75  int val,
78  const struct timespec
79  *timeout,
81  int *uaddr2,
82  int val3
83  ) {
84  return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
85 }
86 
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
91 
114 int p00_futex_wait_once(int* uaddr, int val) {
115  int ret = p00_futex(uaddr, FUTEX_WAIT, val);
116  if (P99_UNLIKELY(ret < 0)) {
117  ret = errno;
118  errno = 0;
119  }
120  return ret;
121 }
122 
173 #define P00_FUTEX_WAIT(ADDR, NAME, EXPECTED) \
174 do { \
175  register int volatile*const p = (int volatile*)(ADDR); \
176  for (;;) { \
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)) { \
181  assert(!ret); \
182  } \
183  } \
184  } while (false)
185 
197 int p00_futex_wake(int* uaddr, int wakeup) {
198  int ret = p00_futex(uaddr, FUTEX_WAKE, wakeup);
199  return ret;
200 }
201 
209 int p00_futex_signal(int* uaddr) {
210  return p00_futex_wake(uaddr, 1);
211 }
212 
221 int p00_futex_broadcast(int* uaddr) {
222  return p00_futex_wake(uaddr, INT_MAX);
223 }
224 
225 # ifndef P99_FUTEX_INITIALIZER
226 # define P99_FUTEX_INITIALIZER(INITIAL) ATOMIC_VAR_INIT(INITIAL)
227 # endif
228 
230 p99_futex* p99_futex_init(p99_futex* p00_c, unsigned p00_ini) {
231  if (p00_c) {
232  atomic_init(p00_c, p00_ini);
233  }
234  return p00_c;
235 }
236 
238 void p99_futex_destroy(p99_futex* p00_c) {
239  if (p00_c) {
240  atomic_store(p00_c, UINT_MAX);
241  }
242 }
243 
245 unsigned p99_futex_load(p99_futex volatile* p00_fut) {
246  return atomic_load(p00_fut);
247 }
248 
250 void p99_futex_wakeup(p99_futex volatile* p00_cntp,
251  unsigned p00_wmin, unsigned p00_wmax) {
252  if (p00_wmax < p00_wmin) p00_wmax = p00_wmin;
253  if (p00_wmax) {
254  unsigned volatile*const p00_cnt = (unsigned*)p00_cntp;
255  static_assert(sizeof *p00_cntp == sizeof *p00_cnt,
256  "linux futex supposes that there is no hidden lock field");
257  for (;;) {
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;
261  p00_wmax -= p00_wok;
262  p00_wmin -= p00_wok;
263  }
264  }
265 }
266 
268 void p99_futex_wait(p99_futex volatile* p00_cntp) {
269  unsigned volatile*const p00_cnt = (unsigned*)p00_cntp;
270  static_assert(sizeof *p00_cntp == sizeof *p00_cnt,
271  "linux futex supposes that there is no hidden lock field");
272  for (;;) {
273  unsigned p00_act = *p00_cnt;
274  register int p00_ret = p00_futex_wait_once((int*)p00_cnt, p00_act);
275  switch (p00_ret) {
276  default: assert(!p00_ret);
277  case 0: return;
278  // Allow for different val or spurious wake ups
279  case EWOULDBLOCK: ;
280  case EINTR: ;
281  }
282  }
283 }
284 
285 
287 unsigned p99_futex_add(p99_futex volatile* futex, unsigned p00_hmuch,
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))
293  p99_futex_wakeup(futex, p00_wmin, p00_wmax);
294  return p00_act;
295 }
296 
298 unsigned p99_futex_exchange(p99_futex volatile* futex, unsigned p00_desired,
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))
303  p99_futex_wakeup(futex, p00_wmin, p00_wmax);
304  return p00_act;
305 }
306 
307 #ifndef P99_FUTEX_COMPARE_EXCHANGE
308 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_FUTEX_COMPARE_EXCHANGE, 1)
309 # define P99_FUTEX_COMPARE_EXCHANGE(FUTEX, ACT, EXPECTED, DESIRED, WAKEMIN, WAKEMAX) \
310 do { \
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; \
316  for (;;) { \
317  register unsigned const ACT = p00Mact; \
318  if (P99_LIKELY(EXPECTED)) { \
319  register unsigned const p00Mdes = (DESIRED); \
320  /* This will only fail if there is contention on the futex, so we then try */ \
321  /* again, immediately. */ \
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); \
327  break; \
328  } \
329  } else { \
330  register int p00Mret = p00_futex_wait_once((int*)p00Mcnt, ACT); \
331  switch (p00Mret) { \
332  default: assert(!p00Mret); \
333  case 0: ; \
334  /* Allow for different val or spurious wake ups */ \
335  case EWOULDBLOCK: ; \
336  case EINTR: ; \
337  } \
338  p00Mact = *p00Mcnt; \
339  } \
340  } \
341  } while (false)
342 #endif
343 
344 #endif
345 
350 #endif
p99_futex::p99_futex_destroy
void p99_futex_destroy(p99_futex *p00_c)
Destroy an p99_futex object.
p99_futex
A counter similar to a conditional variable that allows atomic increment and decrement and to wait fo...
Definition: p99_futex.h:163
static_assert
#define static_assert(EXPR, DIAGSTR)
Evaluate expression EXPR at compile time and ensure that it is fulfilled.
Definition: p99_compiler.h:695
P99_IN_RANGE
#define P99_IN_RANGE(R, S, L)
check if R is in the range [S, S + L)
Definition: p99_generic.h:1499
p99_futex::p99_futex_exchange
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.
p99_inline
#define p99_inline
Try to force a function always to be inlined.
Definition: p99_compiler.h:496
p99_futex::P99_FUTEX_COMPARE_EXCHANGE
#define P99_FUTEX_COMPARE_EXCHANGE(FUTEX, ACT, EXPECTED, DESIRED, WAKEMIN, WAKEMAX)
a catch all macro to operate on p99_futex
Definition: p99_futex.h:563
P99_DEFARG_DOCU
#define P99_DEFARG_DOCU(NAME)
Provide a documentation section to a function defined with P99_CALL_DEFARG.
Definition: p99_defarg.h:318
p99_futex::p99_futex_load
unsigned p99_futex_load(p99_futex volatile *p00_fut)
Obtain the value of futex p00_fut atomically.
p99_atomic.h
p99_posix_default.h
p99_futex::p99_futex_wakeup
void p99_futex_wakeup(p99_futex volatile *p00_fut, unsigned p00_wmin, unsigned p00_wmax)
Wake up threads that are waiting for a futex.
p99_futex::p99_futex_wait
void p99_futex_wait(p99_futex volatile *p00_fut)
Unconditionally wait for futex p00_fut.
p99_futex::p99_futex_add
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.
P99_UNLIKELY
#define P99_UNLIKELY(...)
Mark the conditional expression as being unlikely.
Definition: p99_compiler.h:994
p99_futex::p99_futex_init
p99_futex * p99_futex_init(p99_futex *p00_c, unsigned p00_ini)
Initialize an p99_futex object.
p99_threads.h
errno
errno
Definition: p99_constraint.h:199