std :: condition_variable wait_until令人惊讶的行为 [英] std::condition_variable wait_until surprising behaviour

查看:185
本文介绍了std :: condition_variable wait_until令人惊讶的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用VS2013构建时,为条件变量的 wait_until 指定 time_point :: max()会导致立即超时.

Building with VS2013, specifying time_point::max() to a condition variable's wait_until results in an immediate timeout.

这似乎是不直观的-我会天真地希望 time_point :: max()无限期地等待(或至少很长一段时间).任何人都可以确认这是否已记录,预期的行为或特定于MSVC的内容吗?

This seems unintuitive - I would naively expect time_point::max() to wait indefinitely (or at least a very long time). Can anyone confirm if this is documented, expected behaviour or something specific to MSVC?

下面的示例程序;请注意,将 time_point :: max()替换为 now + std :: chrono :: hours(1)会产生预期的行为( wait_for 退出一次通知了简历,没有超时)

Sample program below; note replacing time_point::max() with now + std::chrono::hours(1) gives the expected behaviour (wait_for exits once cv is notified, with no timeout)


#include <condition_variable>
#include <mutex>
#include <chrono>
#include <future>
#include <functional>

void fire_cv( std::mutex *mx, std::condition_variable *cv )
{
    std::unique_lock<std::mutex> lock(*mx);
    printf("firing cv\n");
    cv->notify_one();
}

int main(int argc, char *argv[])
{
    std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();

    std::condition_variable test_cv;
    std::mutex test_mutex;

    std::future<void> s;
    {
        std::unique_lock<std::mutex> lock(test_mutex);
        s = std::async(std::launch::async, std::bind(fire_cv, &test_mutex, &test_cv));
        printf("blocking on cv\n");
        std::cv_status result = test_cv.wait_until( lock, std::chrono::steady_clock::time_point::max() );

        //std::cv_status result = test_cv.wait_until( lock, now + std::chrono::hours(1) ); // <--- this works as expected!
        printf("%s\n", (result==std::cv_status::timeout) ? "timeout" : "no timeout");
    }
    s.wait();

    return 0;
}

推荐答案

我调试了MSCV 2015的实现,并且 wait_until 在内部调用了 wait_for ,实现方式如下:

I debugged MSCV 2015's implementation, and wait_until calls wait_for internally, which is implemented like this:

template<class _Rep,
        class _Period>
        _Cv_status wait_for(
            unique_lock<mutex>& _Lck,
            const chrono::duration<_Rep, _Period>& _Rel_time)
        {   // wait for duration
        _STDEXT threads::xtime _Tgt = _To_xtime(_Rel_time); // Bug!
        return (wait_until(_Lck, &_Tgt));
        }

此处的错误是 _To_xtime 溢出,导致未定义的行为,结果是 time_point :

The bug here is that _To_xtime overflows, which results in undefined behavior, and the result is a negative time_point:

template<class _Rep,
    class _Period> inline
    xtime _To_xtime(const chrono::duration<_Rep, _Period>& _Rel_time)
    {   // convert duration to xtime
    xtime _Xt;
    if (_Rel_time <= chrono::duration<_Rep, _Period>::zero())
        {   // negative or zero relative time, return zero
        _Xt.sec = 0;
        _Xt.nsec = 0;
        }
    else
        {   // positive relative time, convert
        chrono::nanoseconds _T0 =
            chrono::system_clock::now().time_since_epoch();
        _T0 += chrono::duration_cast<chrono::nanoseconds>(_Rel_time); //Overflow!
        _Xt.sec = chrono::duration_cast<chrono::seconds>(_T0).count();
        _T0 -= chrono::seconds(_Xt.sec);
        _Xt.nsec = (long)_T0.count();
        }
    return (_Xt);
    }

std :: chrono :: nanoseconds 默认情况下将其值存储在 long long 中,因此在定义后, _T0 具有一个 1'471'618'263'082'939'000 的值(此变化明显).添加 _Rel_time ( 9'223'244'955'544'505'510 )肯定会导致签名溢出.

std::chrono::nanoseconds by default stores its value in a long long, and so after its definition, _T0 has a value of 1'471'618'263'082'939'000 (this changes obviously). Adding _Rel_time (9'223'244'955'544'505'510) results definitely in signed overflow.

我们已经排除了所有可能的 time_point ,所以会发生超时.

We have already passed every negative time_point possible, so a timeout happens.

这篇关于std :: condition_variable wait_until令人惊讶的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆