Skip to content

<condition_variable>: condition_variable_any::wait_for returns cv_status::timeout when the elapsed time is shorter than requested #4723

@cpplearner

Description

@cpplearner

Describe the bug

Revealed by libc++ test test/std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp.

[thread.condvarany.wait]/13:

Returns: cv_status​::​timeout if the relative timeout ([thread.req.timing]) specified by rel_time expired, otherwise cv_status​::​no_timeout.

But MSVC STL's condition_variable_any::wait_for sometimes returns cv_status​::​timeout even though the elapsed time (measured by high_resolution_clock) is shorter than the requested timeout.

Command-line test case

D:\test>type test-condvarany.cpp
#include <condition_variable>
#include <atomic>
#include <cassert>
#include <chrono>
#include <mutex>
#include <thread>
#include <print>

template <class Mutex>
struct MyLock : std::unique_lock<Mutex> {
  using std::unique_lock<Mutex>::unique_lock;
};

template <class Function>
std::chrono::microseconds measure(Function f) {
  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
  f();
  std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}

template <class Lock>
void test() {
  using Mutex = typename Lock::mutex_type;

  // Test unblocking via a timeout.
  //
  // To test this, we create a thread that waits on a condition variable
  // with a certain timeout, and we never awaken it. To guard against
  // spurious wakeups, we wait again whenever we are awoken for a reason
  // other than a timeout.
  {
    auto timeout = std::chrono::milliseconds(250);
    std::condition_variable_any cv;
    Mutex mutex;

    std::thread t1([&] {
      Lock lock(mutex);
      std::cv_status result;
      do {
        auto elapsed = measure([&] { result = cv.wait_for(lock, timeout); });
        if (result == std::cv_status::timeout)
          if (elapsed < timeout) {
            std::println("elapsed: {}", elapsed);
            std::println("timeout: {}", timeout);
          }

      } while (result != std::cv_status::timeout);
    });

    t1.join();
  }
}

int main(int, char**) {
  test<std::unique_lock<std::mutex>>();
  test<std::unique_lock<std::timed_mutex>>();
  test<MyLock<std::mutex>>();
  test<MyLock<std::timed_mutex>>();
  return 0;
}

D:\test>cl /EHs /std:c++latest test-condvarany.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.41.33901 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

/std:c++latest is provided as a preview of language features from the latest C++
working draft, and we're eager to hear about bugs and suggestions for improvements.
However, note that these features are provided as-is without support, and subject
to changes or removal as the working draft evolves. See
https://go.microsoft.com/fwlink/?linkid=2045807 for details.

test-condvarany.cpp
Microsoft (R) Incremental Linker Version 14.41.33901.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:test-condvarany.exe
test-condvarany.obj

D:\test>.\test-condvarany.exe
elapsed: 249758us
timeout: 250ms

(You might need to execute the program several times to see the output.)

Expected behavior

.\test-condvarany.exe should consistently produce no output

STL version

v17.11.0-pre.1.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions