Linux futex_wake如何返回0

Linux futex_wake如何返回0,linux,semaphore,futex,Linux,Semaphore,Futex,我使用futex实现了信号量。以下程序经常在sem_post()中的断言处失败。虽然返回值应为1,但有时返回0。这怎么会发生 当我使用POSIX信号量时,程序总是成功完成 我使用的是Linux 2.6.32-642.6.1.el6.x86_64 #include <cstdio> #include <cstdlib> #include <cassert> #include <ctime> #include <linux/futex.h>

我使用futex实现了信号量。以下程序经常在sem_post()中的断言处失败。虽然返回值应为1,但有时返回0。这怎么会发生

当我使用POSIX信号量时,程序总是成功完成

我使用的是Linux 2.6.32-642.6.1.el6.x86_64

#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <linux/futex.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

using namespace std;

#if 0
 #include <semaphore.h>
#else
typedef volatile int sem_t;

void sem_init(sem_t* sem, int shared, int value)
{
    *sem = value;
}

void sem_post(sem_t* sem)
{
    while (1)
    {
        int value = *sem;
        if (__sync_bool_compare_and_swap(sem, value, value >= 0 ? value+1 : 1))
        {
            if (value < 0)      // had contender
            {
                int r = syscall(SYS_futex, sem, FUTEX_WAKE, 1, NULL, 0, 0);
                if (r != 1)
                    fprintf(stderr, "post r=%d err=%d sem=%d %d\n", r,errno,value,*sem);
                assert(r == 1);
            }
            return;
        }
    }
}

int sem_wait(sem_t* sem)
{
    while (1)
    {
        int value = *sem;
        if (value > 0   // positive means no contender
            && __sync_bool_compare_and_swap(sem, value, value-1))
            return 0;
        if (value <= 0
            && __sync_bool_compare_and_swap(sem, value, -1))
        {
            int r= syscall(SYS_futex, sem, FUTEX_WAIT, -1, NULL, 0, 0);
            if (!r) {
                assert(__sync_fetch_and_sub(sem, 1) > 0);
                return 0;
            }
            printf("wait r=%d errno=%d sem=%d %d\n", r,errno, value,*sem);
        }
    }
}

void sem_getvalue(sem_t* sem, int* value)
{
    *value = *sem;
}

#endif

// return current time in ns
unsigned long GetTime()
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return ts.tv_sec*1000000000ul + ts.tv_nsec;
}

void Send(sem_t* sem, unsigned count)
{
    while (count--)
        sem_post(sem);
}

void Receive(sem_t* sem, unsigned count)
{
    while (count--)
        sem_wait(sem);
}


int main()
{
    sem_t* sem = reinterpret_cast<sem_t*>(mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0));
    assert(sem != MAP_FAILED);
    sem_init(sem, 1, 0);
    unsigned count = 10485760;

    int pid = fork();
    assert(pid != -1);
    if (!pid)   // child
    {
        Send(sem, count);
        _exit(EXIT_SUCCESS);
    }
    else    // parent
    {
        unsigned long t0 = GetTime();
        Receive(sem, count);
        printf("t=%g ms\n", (GetTime()-t0)*1e-6);
        wait(NULL);
        int v;
        sem_getvalue(sem, &v);
        assert(v == 0);
    }
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
#如果0
#包括
#否则
typedef volatile int sem\t;
void sem_init(sem_t*sem,int共享,int值)
{
*sem=数值;
}
空扫描电镜柱(扫描电镜t*扫描电镜)
{
而(1)
{
int值=*sem;
如果(uuu sync_ubool_ucompare_u和_uswap(sem,值,值>=0?值+1:1))
{
if(值<0)//有竞争者
{
int r=syscall(SYS_futex,sem,futex_WAKE,1,NULL,0,0);
如果(r!=1)
fprintf(stderr,“post r=%d err=%d sem=%d%d\n”,r,errno,value,*sem);
断言(r==1);
}
返回;
}
}
}
int sem_wait(sem_t*sem)
{
而(1)
{
int值=*sem;
if(值>0//正值表示没有竞争者
&&同步、布尔、比较和交换(sem、值、值-1))
返回0;
if(值为0);
返回0;
}
printf(“wait r=%d errno=%d sem=%d%d\n”,r,errno,value,*sem);
}
}
}
void sem_getvalue(sem_t*sem,int*value)
{
*值=*sem;
}
#恩迪夫
//返回当前时间(单位:ns)
无符号长GetTime()
{
结构timespects;
时钟获取时间(时钟实时,&ts);
返回ts.tv\u sec*1000000000ul+ts.tv\u nsec;
}
无效发送(sem_t*sem,未签名计数)
{
而(计数--)
sem_post(sem);
}
无效接收(sem_t*sem,未签名计数)
{
而(计数--)
sem_wait(sem);
}
int main()
{
sem|t*sem=reinterpret|u cast(mmap(NULL,sizeof(sem|t),PROT|u READ | PROT|u WRITE,MAP|u匿名| MAP|u SHARED,-1,0));
断言(sem!=MAP_失败);
sem_init(sem,1,0);
无符号计数=10485760;
int-pid=fork();
断言(pid!=-1);
if(!pid)//child
{
发送(扫描电镜、计数);
_退出(退出成功);
}
else//parent
{
无符号长t0=GetTime();
接收(扫描电镜、计数);
printf(“t=%g ms\n”,(GetTime()-t0)*1e-6);
等待(空);
INTV;
sem_getvalue(sem,&v);
断言(v==0);
}
}

似乎
\u同步\u bool\u比较\u和\u交换(sem,value,-1)
\u同步\u获取\u和\u sub(sem,1)
是有问题的。我们需要记住,
sem_wait
可以由多个线程同时调用(尽管在您的测试用例中只有一个线程调用它)

如果我们能够负担繁忙轮询的开销,我们可以删除
futex
,并生成以下代码。它也比
futex
版本快(t=347 ms,而
futex
版本的t=914 ms)

代码的工作原理如下:共享变量
*sem
始终为非负。当一个线程将信号量从0发送到1时,所有等待该信号量的线程都可以尝试,但只有一个线程将成功地
比较和交换

调用
系统调用(SYS\u futex,sem,futex\u WAKE,1,NULL,0)
在没有线程等待
sem
时将返回0。在您的代码中,这是可能的,因为当
*sem
为负数时,您调用
sem\u post
中的futex行,如果没有任何线程实际处于休眠状态,则可能是这种情况:


如果调用
sem\u wait
*sem
为零,则继续执行
\u sync\u bool\u compare\u和\u swap(sem,value,-1)
*sem
设置为-1。然而,此时该线程尚未休眠。因此,当另一个线程在该点调用
sem\u post
时(在调用
sem\u wait
的线程进入
futex
syscall之前),您的断言将失败。

您的代码可以工作,但它没有回答我最初的问题。我知道sched_yield()更快,但在cpu使用率更重要的情况下(没有足够的内核,对延迟的要求更低),futex是首选。在我的代码中,uuu sync_bool_compare_和_swap(sem,value,-1)以及u sync_fetch_和_sub(sem,1)在一个线程中,那么assert()怎么会失败呢?
void sem_post(sem_t* sem)
{
    int value = __sync_fetch_and_add(sem, 1);
}

int sem_wait(sem_t* sem)
{
    while (1)
    {
        int value = *sem;
        if (value > 0) // positive means no contention
        {
            if (__sync_bool_compare_and_swap(sem, value, value-1)) {
                return 0; // success
            }
        }
        // yield the processor to avoid deadlock
        sched_yield();
    }
}