futex.h 4.02 KB
#ifndef FUTEXX_H
#define FUTEXX_H

#include <linux/futex.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

static __always_inline int
futex(const int *uaddr, const int futex_op, const int val,
		const struct timespec *timeout, const int *uaddr2, const unsigned int val3) {
	return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
}

static __always_inline __attribute__ ((__noreturn__)) void
futex_fatal_error (const int err) {
	fprintf(stderr, "The futex facility returned an unexpected error code. (%d, %d)\n", err, errno);
	_exit (127);
}

static __always_inline int
futex_wait_bitset (const int *futex_word,
		const int expected,
		const struct timespec *abstime, const unsigned int bitset, const int priv) {
	if (abstime != NULL && abstime->tv_sec < 0)
		return ETIMEDOUT;
	const int op = (FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME | (priv ? FUTEX_PRIVATE_FLAG : 0) );
	const int err=futex(futex_word, op, expected, abstime, NULL, bitset);

	if (err>=0)
		return err;

	switch (errno) {
		case 0:
		case EAGAIN:
		case EINTR:
		case ETIMEDOUT:
			return err;
		case EFAULT: /* Must have been caused by a glibc or application bug.  */
		case EINVAL: /* Either due to wrong alignment or due to the timeout not
		                being normalized.  Must have been caused by a glibc or
		                application bug.  */
		case ENOSYS: /* Must have been caused by a glibc bug.  */
		default:
			futex_fatal_error (err);
	}
}

static __always_inline int
futex_wake_bitset (const int *futex_word, const int processes_to_wake, const unsigned int bitset, const int priv) {
	const int op = (FUTEX_WAKE_BITSET | (priv ? FUTEX_PRIVATE_FLAG : 0) );
	const int err=futex(futex_word, op, processes_to_wake, NULL, NULL, bitset);

	if (err >= 0)
		return err;
	switch (errno) {
		case EFAULT: /* Could have happened due to memory reuse.  */
		case EINVAL: /* Could be either due to incorrect alignment (a bug in
		                glibc or in the application) or due to memory being
		                reused for a PI futex.  We cannot distinguish between the
		                two causes, and one of them is correct use, so we do not
		                act in this case.  */
			return err;
		case ENOSYS: /* Must have been caused by a glibc bug.  */
		             /* No other errors are documented at this time.  */
		default:
			futex_fatal_error (err);
	}
}

static __always_inline int
futex_wait (const int *futex_word,
		const int expected,
		const struct timespec *abstime, const int priv) {
	if (abstime != NULL && abstime->tv_sec < 0)
		return ETIMEDOUT;
	const int op = (FUTEX_WAIT | (priv ? FUTEX_PRIVATE_FLAG : 0) );
	const int err=futex(futex_word, op, expected, abstime, NULL, 0);

	if (err>=0)
		return err;

	switch (errno) {
		case 0:
		case EAGAIN:
		case EINTR:
		case ETIMEDOUT:
			return err;
		case EFAULT: /* Must have been caused by a glibc or application bug.  */
		case EINVAL: /* Either due to wrong alignment or due to the timeout not
		                being normalized.  Must have been caused by a glibc or
		                application bug.  */
		case ENOSYS: /* Must have been caused by a glibc bug.  */
		default:
			futex_fatal_error (err);
	}
}

static __always_inline int
futex_wake (const int *futex_word, const int processes_to_wake, const int priv) {
	const int op = (FUTEX_WAKE | (priv ? FUTEX_PRIVATE_FLAG : 0) );
	const int err=futex(futex_word, op, processes_to_wake, NULL, NULL, 0);

	if (err >= 0)
		return err;
	switch (errno) {
		case EFAULT: /* Could have happened due to memory reuse.  */
		case EINVAL: /* Could be either due to incorrect alignment (a bug in
		                glibc or in the application) or due to memory being
		                reused for a PI futex.  We cannot distinguish between the
		                two causes, and one of them is correct use, so we do not
		                act in this case.  */
			return err;
		case ENOSYS: /* Must have been caused by a glibc bug.  */
		             /* No other errors are documented at this time.  */
		default:
			futex_fatal_error (err);
	}
}

#endif /*FUTEXX_H*/