You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二进制信号量Wait函数偶尔提前返回问题排查求助

二进制信号量Wait函数偶尔提前返回的问题分析

问题描述

实现的二进制信号量有时无法等待指定时长,存在等待时间不足的情况,需定位binarySemaphoreWait函数偶尔提前返回的原因。

代码实现

#define SEMAPHORE_MAXWAIT   -1
#define SEMAPHORE_NOWAIT    0

typedef struct binary_semaphore {
    pthread_mutex_t mutex;
    pthread_cond_t condvar;
    bool variable;
}binary_semaphore_t;


static struct timespec *timespec_addms(struct timespec *ts, unsigned ms)
{
    uint64_t nsec;
    if(ts)
    {
        ts -> tv_sec += ms / 1000;
        nsec = ts -> tv_nsec + (ms % 1000) * 1000000ULL;
        ts -> tv_sec += nsec / 1000000000ULL;
        ts -> tv_nsec = nsec % 1000000000ULL;
    }
    return ts;
}

static int mutex_lockwait(pthread_mutex_t *mutex, unsigned wait)
{
    int result = 0;
    int line;
    struct timespec timeOut;

    if(mutex)
    {
        if(result = clock_gettime(CLOCK_REALTIME, &timeOut))
        {
            line = __LINE__;
            goto exit_cleanup;
        }

        timespec_addms(&timeOut, wait);

        switch(wait)
        {
            case SEMAPHORE_NOWAIT:
                result = pthread_mutex_trylock(mutex);
                line = __LINE__;
                break;
            case SEMAPHORE_MAXWAIT:
                result = pthread_mutex_lock(mutex);
                line = __LINE__;
                break;
            default:
                result = pthread_mutex_timedlock(mutex, &timeOut);
                line = __LINE__;
                break;
        }
    }
    else result = EFAULT;

    exit_cleanup:
    if(result)
    {
        if(result != EBUSY)
        {
            printError("[MUTEX]", line, __FUNCTION__);
        }
    }
    return result;
}


static int64_t timespec_diff(const struct timespec *t1, const struct timespec *t2)
{
    int64_t nanodiff = -1;
    //t1 has to be later than t2
    if(t1 -> tv_sec >= t2 -> tv_sec)
    {
        nanodiff = (t1 -> tv_sec - t2 -> tv_sec) * 1000000000ULL;
        if(t1 -> tv_nsec > t2 -> tv_nsec)
        {
            nanodiff += t1 -> tv_nsec - t2 -> tv_nsec;
        }
        else
        {
            if(!nanodiff)
            {
                nanodiff = -1;
            }
            else
            {
                nanodiff -= 1000000000ULL + (t1 -> tv_nsec - t2 -> tv_nsec);

            }
        }
    }
    return nanodiff;
}

static struct timespec nanoToTimespec(int64_t nanosec)
{
    return (struct timespec){.tv_sec = nanosec / 1000000000ULL, .tv_nsec = nanosec % 1000000000ULL};
}

int binarySemaphoreWait(struct binary_semaphore *p, unsigned wait)
{
    int result= -1;

    struct timespec ts, ts1;
    
    if(!p) goto function_return;

    if((result = clock_gettime(CLOCK_REALTIME, &ts))) goto function_return;

    if((result = mutex_lockwait(&p -> mutex, wait))) goto function_return;
    while (!p->variable)
    {
        if((result = clock_gettime(CLOCK_REALTIME, &ts1))) goto cleanup_exit;
        int64_t nanodiff = timespec_diff(&ts1, &ts);
        if(nanodiff > 0 && (nanodiff / 1000000ULL) < wait)
        {
            timespec_addms(&ts, wait - nanodiff / 1000000ULL);
            pthread_cond_timedwait(&p->condvar, &p->mutex, &ts);
        }
    }
    p->variable = false;

    cleanup_exit:
    pthread_mutex_unlock(&p->mutex);
    function_return:
    return result;
}


int binarySemaphorePost(struct binary_semaphore *p)
{
    int result =  -1;
    
    if(!p) goto cleanup_exit;
    
    result = mutex_lockwait(&p -> mutex, SEMAPHORE_MAXWAIT);

    if(result) goto cleanup_exit;
    if (p->variable)
    {
        result = EAGAIN;
        goto cleanup_exit;
    }

    p->variable = true;

    if((result = pthread_cond_signal(&p->condvar))) goto cleanup_exit;
    if((result = pthread_mutex_unlock(&p->mutex))) goto cleanup_exit;

    cleanup_exit:
    return result;
}

问题原因分析

1. 超时时间计算逻辑错误

binarySemaphoreWait中,初始ts是调用clock_gettime获取的当前时间,但mutex_lockwait可能已经消耗了部分等待时长(比如调用pthread_mutex_timedlock等待锁的过程)。后续计算剩余时间时,代码通过timespec_diff得到已耗时,然后将ts更新为ts + 剩余时间,这相当于把超时时间向后累加,而非使用固定的绝对超时时间(初始时间+指定等待时长)。这种方式会导致超时逻辑混乱,在时间差计算出错时直接触发提前退出。

2. pthread_cond_timedwait返回值未处理

代码调用pthread_cond_timedwait后没有检查返回值:

  • 如果函数返回ETIMEDOUT,说明已到超时时间,应该退出循环并返回超时错误;
  • 如果返回其他错误(比如EINVAL),也需要处理并退出。
    当前逻辑下,即使超时,循环仍会继续,可能错误判断信号量状态,导致提前返回。

3. timespec_diff函数逻辑缺陷

当t1的秒数等于t2,但纳秒数小于等于t2时,函数返回-1。此时binarySemaphoreWait中的条件nanodiff > 0不成立,会跳过pthread_cond_timedwait调用,直接继续循环。而while (!p->variable)的条件仍然成立,会直接退出循环,导致信号量未被唤醒也未等待足够时长就提前返回。

4. 类型不匹配导致的分支错误

SEMAPHORE_MAXWAIT定义为-1,但mutex_lockwait的wait参数是unsigned类型。-1转换为unsigned后是UINT_MAX,与switch中case SEMAPHORE_MAXWAIT的int类型-1不匹配,会走到default分支,错误调用pthread_mutex_timedlock而非pthread_mutex_lock,可能导致锁等待超时。

修复建议

  1. 修正超时时间计算:
    在binarySemaphoreWait开头直接计算固定的绝对超时时间,后续循环不再修改:

    struct timespec timeout_ts;
    if((result = clock_gettime(CLOCK_REALTIME, &timeout_ts))) goto function_return;
    timespec_addms(&timeout_ts, wait);
    

    循环中直接使用timeout_ts调用pthread_cond_timedwait。

  2. 处理pthread_cond_timedwait返回值:

    result = pthread_cond_timedwait(&p->condvar, &p->mutex, &timeout_ts);
    if (result == ETIMEDOUT) {
        // 超时退出
        goto cleanup_exit;
    } else if (result != 0) {
        // 其他错误处理
        goto cleanup_exit;
    }
    
  3. 修复timespec_diff函数:
    确保正确计算时间差,无论纳秒数关系如何:

    static int64_t timespec_diff(const struct timespec *t1, const struct timespec *t2)
    {
        int64_t sec_diff = (int64_t)t1->tv_sec - (int64_t)t2->tv_sec;
        int64_t nsec_diff = (int64_t)t1->tv_nsec - (int64_t)t2->tv_nsec;
        return sec_diff * 1000000000LL + nsec_diff;
    }
    

    返回值为t1 - t2的纳秒差,正数表示t1晚于t2。

  4. 修正参数类型匹配问题:
    将wait参数改为int类型,确保与SEMAPHORE_MAXWAIT、SEMAPHORE_NOWAIT的类型一致,避免switch分支判断错误。

内容的提问来源于stack exchange,提问作者0___________

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 22:07:04