Skip to content

Instantly share code, notes, and snippets.

@andrealmeid
Created May 23, 2024 19:43
Show Gist options
  • Save andrealmeid/f0b8c93a3c7a5c50458247c47f7078e1 to your computer and use it in GitHub Desktop.
Save andrealmeid/f0b8c93a3c7a5c50458247c47f7078e1 to your computer and use it in GitHub Desktop.
/*
* futex2_spin example, by André Almeida <andrealmeid@igalia.com>
*
* Partially inspired by Steven Rostedt benchmark from:
* https://lore.kernel.org/all/20231025235413.597287e1@gandalf.local.home/2-extend-sched.c
*
* gcc spin.c -o spin
*/
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <linux/futex.h>
#include <linux/sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#define __NR_futex_wake 454
#define __NR_futex_wait 455
#define FUTEX2_SPIN 0x08
#define STACK_SIZE (1024 * 1024)
#define FUTEX2_SIZE_U32 0x02
#define FUTEX2_PRIVATE FUTEX_PRIVATE_FLAG
#define timeout_ns 90000000
#define sec2usec(sec) (sec * 1000000ULL)
#define usec2sec(usec) (usec / 1000000ULL)
#define WAKE_WAIT_US 100
#define CHILD_LOOPS 500000
void *futex;
static unsigned long long get_time(void)
{
struct timeval tv;
unsigned long long time;
gettimeofday(&tv, NULL);
time = sec2usec(tv.tv_sec);
time += tv.tv_usec;
return time;
}
static inline int futex2_wake(volatile void *uaddr, unsigned long mask, int nr, unsigned int flags)
{
return syscall(__NR_futex_wake, uaddr, mask, nr, flags);
}
static inline int futex2_wait(volatile void *uaddr, unsigned long val, unsigned long mask,
unsigned int flags, struct timespec *timo, clockid_t clockid)
{
return syscall(__NR_futex_wait, uaddr, val, mask, flags, timo, clockid);
}
void waiter_fn(unsigned int f, unsigned long long *total)
{
struct timespec to;
unsigned int flags = FUTEX2_PRIVATE | FUTEX2_SIZE_U32;
flags |= f;
unsigned long long start, end, delta;
uint32_t child_pid = *(uint32_t *) futex;
clock_gettime(CLOCK_MONOTONIC, &to);
to.tv_nsec += timeout_ns;
if (to.tv_nsec >= 1000000000) {
to.tv_sec++;
to.tv_nsec -= 1000000000;
}
start = get_time();
if (futex2_wait(futex, child_pid, ~0U, flags, &to, CLOCK_MONOTONIC))
printf("waiter failed errno %d\n", errno);
end = get_time();
delta = end - start;
*total = *total + delta;
}
int function(int n)
{
return n + n;
}
static void *thread_fn(void *arg)
{
int i, n = 2;
uint32_t *tid = (uint32_t *) arg;
*tid = gettid();
for (i = 0; i < CHILD_LOOPS * 10; i++)
n = function(n);
futex2_wake(futex, ~0U, 1, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG);
return NULL;
}
#define NR_RUNS 1000
void run_test_pthread(unsigned int flags, uint32_t *child_pid)
{
unsigned long long total_wait = 0;
pthread_t thread;
int i;
for (i = 0; i < NR_RUNS; i++) {
pthread_create(&thread, NULL, &thread_fn, child_pid);
usleep(WAKE_WAIT_US);
waiter_fn(flags, &total_wait);
pthread_join(thread, NULL);
}
printf("Total wait time: %llu usecs\n", total_wait);
}
int main()
{
uint32_t child_pid = 0;
futex = &child_pid;
puts("Testing with FUTEX2_SPIN | FUTEX_WAIT");
run_test_pthread(FUTEX2_SPIN, &child_pid);
sleep(1);
puts("Testing with FUTEX_WAIT");
run_test_pthread(0, &child_pid);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment