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;
{
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 * ();
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);
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_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,是个小问题了。
if (abstime.tv_nsec >= BILLION)
{
abstime.tv_sec += abstime.tv_nsec / BILLION;
abstime.tv_nsec %= BILLION;
}
此时的判断也可以去掉,如果传入的 timeout 的 tv_usec 比较小,则溢出的概率较低,加上这个判断可以减少后面的除法开销。总之这不再是bug,是个小问题了。