使用 do_futex 搞砸了?

发布于 2024-09-16 09:33:13 字数 1494 浏览 5 评论 0原文

我收到一个奇怪的错误。我实现了这两个函数:

int flag_and_sleep(volatile unsigned int *flag)
{
    int res = 0;

    (*flag) = 1;

    res = syscall(__NR_futex, flag, FUTEX_WAIT, 1, NULL, NULL, 0);
    if(0 == res && (0 != (*flag)))
        die("0 == res && (0 != (*flag))");
    return 0;
}

int wake_up_if_any(volatile unsigned int *flag)
{
    if(1 == (*flag))
    {
        (*flag) = 0;
        return syscall(__NR_futex, flag, FUTEX_WAKE, 1, NULL, NULL, 0);
    }
    return 0;
}

并通过运行两个 Posix 线程来测试它们:

static void die(const char *msg)
{
    fprintf(stderr, "%s %u %lu %lu\n", msg, thread1_waits, thread1_count, thread2_count);
    _exit( 1 );
}

volatile unsigned int thread1_waits = 0;

void* threadf1(void *p)
{
    int res = 0;
    while( 1 )
    {
        res = flag_and_sleep( &thread1_waits );
        thread1_count++;
    }
    return NULL;
}

void* threadf2(void *p)
{
    int res = 0;
    while( 1 )
    {
        res = wake_up_if_any( &thread1_waits );
        thread2_count++;
    }

    return NULL;
}

在 thread2 进行了一百万次左右的迭代后,我得到了断言:

./a.out 0 == res && (0!=(*标志))1 261129 1094433

这意味着系统调用 - 以及 do_futex() - 返回 0。Man 说只有在被 do_futex(WAKE) 调用唤醒时才应该这样做。但是在我进行WAKE调用之前,我将标志设置为0。这里看起来标志仍然是1。

这是Intel,这意味着强内存模型。因此,如果在线程 1 中我看到线程 2 中系统调用的结果,那么我还必须看到调用之前线程 2 中写入的结果。

Flag 和所有指向它的指针都是易失性的,所以我不明白 gcc 如何无法读取正确的值。

我很困惑。

谢谢!

I'm getting a weird error. I implemented these two functions:

int flag_and_sleep(volatile unsigned int *flag)
{
    int res = 0;

    (*flag) = 1;

    res = syscall(__NR_futex, flag, FUTEX_WAIT, 1, NULL, NULL, 0);
    if(0 == res && (0 != (*flag)))
        die("0 == res && (0 != (*flag))");
    return 0;
}

int wake_up_if_any(volatile unsigned int *flag)
{
    if(1 == (*flag))
    {
        (*flag) = 0;
        return syscall(__NR_futex, flag, FUTEX_WAKE, 1, NULL, NULL, 0);
    }
    return 0;
}

and test them by running two Posix threads:

static void die(const char *msg)
{
    fprintf(stderr, "%s %u %lu %lu\n", msg, thread1_waits, thread1_count, thread2_count);
    _exit( 1 );
}

volatile unsigned int thread1_waits = 0;

void* threadf1(void *p)
{
    int res = 0;
    while( 1 )
    {
        res = flag_and_sleep( &thread1_waits );
        thread1_count++;
    }
    return NULL;
}

void* threadf2(void *p)
{
    int res = 0;
    while( 1 )
    {
        res = wake_up_if_any( &thread1_waits );
        thread2_count++;
    }

    return NULL;
}

After thread2 has had a million or so iterations, I get the assert fire on me:

./a.out
0 == res && (0 != (*flag)) 1 261129 1094433

This means that the syscall - and thereby do_futex() - returned 0. Man says it should only do so if woken up by a do_futex(WAKE) call. But then before I do a WAKE call, I set the flag to 0. Here it appears that flag is still 1.

This is Intel, which means strong memory model. So if in thread1 I see results from a syscall in thread2, I must also see the results of the write in thread 2 which was before the call.

Flag and all pointers to it are volatile, so I don't see how gcc could fail to read the correct value.

I'm baffled.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

べ映画 2024-09-23 09:33:13

重新进入 WAIT 调用时,就会发生竞争,

(*flag) = 0;

当线程 1 进入完整周期并在线程 2 从 到时

return syscall(__NR_futex, flag, FUTEX_WAKE, 1, NULL, NULL, 0);

因此测试是错误的。

the race happens when thread 1 goes the full cycle and re-enters WAIT call when thread 2 goes from

(*flag) = 0;

to

return syscall(__NR_futex, flag, FUTEX_WAKE, 1, NULL, NULL, 0);

So the test is faulty.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文