I've run into an issue with the Linux futex
syscall (FUTEX_WAIT
operation) sometimes returning early seemingly without cause. The documentation specifies certain conditions that may cause it to return early (without a FUTEX_WAKE
) but these all involve non-zero return values: EAGAIN
if the value at the futex address does not match, ETIMEDOUT
for timed waits that timeout, EINTR
when interrupted by a (non-restarting) signal, etc. But I'm seeing a return value of 0. What, other than FUTEX_WAKE
or the termination of a thread whose set_tid_address
pointer points to the futex, could cause FUTEX_WAIT
to return with a return value of 0?
In case it's useful, the particular futex I was waiting on is the thread tid address (set by the clone
syscall with CLONE_CHILD_CLEARTID
), and the thread had not terminated. My (apparently incorrect) assumption that the FUTEX_WAIT
operation returning 0 could only happen when the thread terminated lead to serious errors in program logic, which I've since fixed by looping and retrying even if it returns 0, but now I'm curious as to why it happened.
Here is a minimal test case:
#define _GNU_SOURCE
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/futex.h>
#include <signal.h>
static char stack[32768];
static int tid;
static int foo(void *p)
{
syscall(SYS_getpid);
syscall(SYS_getpid);
syscall(SYS_exit, 0);
}
int main()
{
int pid = getpid();
for (;;) {
int x = clone(foo, stack+sizeof stack,
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND
|CLONE_THREAD|CLONE_SYSVSEM //|CLONE_SETTLS
|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
|CLONE_DETACHED,
0, &tid, 0, &tid);
syscall(SYS_futex, &tid, FUTEX_WAIT, x, 0);
/* Should fail... */
syscall(SYS_tgkill, pid, tid, SIGKILL);
}
}
Let it run for a while, at it should eventually terminate with Killed
(SIGKILL
), which is only possible if the thread still exists when the FUTEX_WAIT
returns.
Before anyone goes assuming this is just the kernel waking the futex before it finishes destroying the thread (which might in fact be happening in my minimal test case here), please note that in my original code, I actually observed userspace code running in the thread well after FUTEX_WAIT
returned.
FUTEX_WAIT
qualifies the non zero conditions as error conditions, not only diagnostics. Then later it says "In the event of an error, all operations return -1, and set errno to indicate the error." On the other hand the conditions here are not repeated in the ERRORS section. – Shottonstrace
that the "child thread" has not yet called_exit
whenFUTEX_WAIT
returns. – Vergievergil