小心 timeval 和 timespec 不小心溢出了

来源:互联网 发布:mac下载文件夹不见了 编辑:程序博客网 时间:2024/04/20 10:14

timeval 和 timespec 都是 POSIX 的秒以下时间类型,都是一个两个成员的结构,第一个成员是秒数,第二个成员则分别是微秒和纳秒。

之所以用这么个结构,是因为如果直接存储毫秒微秒或者纳秒,32位的整数根本放不下。
要说为什么不像 windows 下那样用 64 位的整数类型,我也觉得很好,但是十年前的编译器多数都没有提供 64 位的整数类型。
至于为什么有这两个用途差不多的时间类型,我也不知道。
 
前几天调试程序,发现带超时的等待偶尔会返回 EINVAL,也就是无效参数。
 
int GetResult(Result *result, const struct timeval *timeout)
{
    struct timespec abstime;
    struct timeval now;
    int ret = 0;
    pthread_mutex_lock(&mutex_);
    gettimeofday(&now, NULL);
    abstime.tv_sec = now.tv_sec + timeout->tv_sec;
    abstime.tv_nsec = 1000 * ();
    while (queue_.empty() && ret != ETIMEDOUT)
        ret = pthread_cond_timedwait(&cond_, &mutex_, &abstime);
    ...
}
问题在这里:当 now.tv_usec + timeout->tv_usec >= 1000000 时,abstime.tv_nsec 也就大于或者等于 10^9 了,直接传给 pthread_condwait 的时候,被认为是无效时间,直接返回 EINVAL 了。出错的概率等于传入的 timeout 中 tv_usec 折合的秒数。
这时候应该进位,秒数加一:
    if (abstime.tv_nsec >= BILLION)
    {
        ++abstime.tv_sec;
        ++abstime.tv_nsec %= BILLION;
    }
如果这样写,传入的 timeval 有问题也能修正,可以更安全一些,这时候应该修正还是报错,这是个哲学问题。
    const long BILLION = 1000000000;
    if (abstime.tv_nsec >= BILLION)
    {
        abstime.tv_sec += abstime.tv_nsec / BILLION;
        abstime.tv_nsec %= BILLION;
    }
此时的判断也可以去掉,如果传入的 timeout 的 tv_usec 比较小,则溢出的概率较低,加上这个判断可以减少后面的除法开销。总之这不再是bug,是个小问题了。
原创粉丝点击