Description
Related: #59268
If the timeout_duration
in the condition_variable::wait_for member function is too big, the function always returns std::cv_status::timeout
, even if no timeout occurred. Consider the following example (Link to comparison of GCC, Clang and MSVC in compiler explorer):
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <limits>
std::mutex m;
std::condition_variable cv;
using namespace std::chrono_literals;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock lk(m);
// changing int to long leads to wrong return value in std::condition_variable::wait_for
auto longWait = std::numeric_limits<long>::max();
auto status = cv.wait_for(lk, std::chrono::milliseconds{longWait});
if (status == std::cv_status::timeout)
std::cout << "timeout occurred" << std::endl;
else if (status == std::cv_status::no_timeout)
std::cout << "notification received, no timeout occurred" << std::endl;
}
int main()
{
std::thread worker(worker_thread);
std::this_thread::sleep_for(2s); // don't notify before thread can aquire lock
std::cout << "notifying cv\n";
cv.notify_one();
worker.join();
}
As soon as the longWait
timeout value takes on a 64bit value, the return value is wrong.
This also seems to apply to GCC's libstdc++ implementation. Only MSVC's implementation delivers the correct and expected results.
Reason
From the libc++ implementation on my machine (Xcode 14.1, Apple clang-1400.0.29.202):
// wait_for
return steady_clock::now() - __c_now < __d ? cv_status::no_timeout :
cv_status::timeout;
steady_clock::now() - __c_now
yields a duration in nanoseconds. So __d
will be converted to nanoseconds
as well, but overflows and returns a negative value in this case.
Here is a minimal example (Compiler Explorer link):
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
using namespace chrono;
using namespace std::chrono_literals;
int main(int argc, char *argv[])
{
steady_clock::time_point t1 = steady_clock::now();
this_thread::sleep_for(1s);
steady_clock::time_point t2 = steady_clock::now();
milliseconds d = milliseconds::max();
auto diff = (t2 -t1).count();
cout << "diff = " << diff << "\n";
cout << "d = " << duration_cast<nanoseconds>(d).count() << "\n";
if (duration_cast<milliseconds>(t2 - t1) < d)
cout << "no timeout\n";
else
cout << "timeout\n";
if (t2 - t1 < d)
cout << "no timeout\n";
else
cout << "timeout\n";
}