Ложное пробуждение системного вызова Linux futex с возвращаемым значением 0?

У меня возникла проблема с системным вызовом Linux futex ( FUTEX_WAIT ), иногда возвращавшимся раньше, по-видимому, без причина. В документации указаны определенные условия, которые могут привести к его преждевременному возврату (без FUTEX_WAKE ), но все они включают ненулевые возвращаемые значения: EAGAIN , если значение в адресе фьютекса не совпадает. , ETIMEDOUT для рассчитанного ожидания этого тайм-аута, EINTR при прерывании сигналом (без перезапуска) и т. Д. Но я вижу возвращаемое значение 0. Что, кроме FUTEX_WAKE или завершение потока, чей указатель set_tid_address указывает на фьютекс, может привести к возврату FUTEX_WAIT с возвращаемым значением 0?

В случае, если он полезно, конкретный фьютекс, который я ожидал, - это tid-адрес потока (установленный системным вызовом clone с помощью CLONE_CHILD_CLEARTID ), а поток не завершился. Мое (очевидно неверное) предположение, что операция FUTEX_WAIT , возвращающая 0, могла произойти только тогда, когда поток завершился, что привело к серьезным ошибкам в логике программы, которые я с тех пор исправляю, повторяя и повторяя попытки, даже если он возвращает 0, но теперь мне любопытно, почему это произошло.

Вот минимальный тестовый пример:

#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);
        }
}

Дайте ему поработать некоторое время, в конечном итоге он должен завершиться с помощью Killed ( SIGKILL ), что возможно, только если поток все еще существует, когда возвращается FUTEX_WAIT .

Прежде чем кто-либо предположит, что это просто ядро ​​пробуждает фьютекс до того, как он завершит уничтожение потока (что на самом деле может происходить в моем минимальном тестовом примере здесь), обратите внимание, что в моем исходном коде я действительно наблюдал запуск кода пользовательского пространства в потоке после возврата FUTEX_WAIT .

6
задан R.. 11 September 2011 в 22:29
поделиться