2

VS2013 でビルドtime_point::max()し、条件変数のwait_until結果に指定するとすぐにタイムアウトになります。

time_point::max()これは直感的ではないように思えます -無期限に (または少なくとも非常に長い時間)待機することを素朴に期待します。これが文書化されているのか、予想される動作なのか、MSVC に固有のものなのか、誰でも確認できますか?

以下のサンプルプログラム。に置き換えるtime_point::max()now + std::chrono::hours(1)、予想される動作が得られることに注意してください(wait_forcv が通知されると、タイムアウトなしで終了します)


#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;
}
4

1 に答える 1

1

MSCV 2015 の実装をデバッグし、次のように実装されている内部wait_until呼び出しを行いました。wait_for

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になることです:

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デフォルトでは、その値を along longに格納するため、その定義後、_T0の値は1'471'618'263'082'939'000(これは明らかに変更されます) になります。_Rel_time( ) を追加する9'223'244'955'544'505'510と、間違いなく符号付きオーバーフローが発生します。

可能なすべての否定をすでに通過しtime_pointたため、タイムアウトが発生します。

于 2016-08-19T14:59:22.160 に答える