Skip to content

condition_variable::wait_for returns wrong value if timeout_duration is too large and overflow occurs #59260

Open
@wAuner

Description

@wAuner

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";
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    libc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.threadingissues related to threading

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions