Skip to content

<semaphore>: counting_semaphore::try_acquire_for() does not block long enough for shorter durations #5574

@StephanTLavavej

Description

@StephanTLavavej

Originally reported as DevCom-10913616 / VSO-2495823.

My cleaned-up repro:

C:\Temp>type meow.cpp
#include <chrono>
#include <print>
#include <semaphore>
#include <stop_token>
#include <thread>
using namespace std;
using namespace std::chrono;

void f(stop_token stop) {
    binary_semaphore new_timer{0};
    for (int i = 0; i < 50 && !stop.stop_requested(); ++i) {
        const auto s = steady_clock::now();
        (void) new_timer.try_acquire_for(800us);
        const auto e = steady_clock::now();
        println("{}", duration_cast<nanoseconds>(e - s));
    }
}
int main() {
    jthread t(f);
    this_thread::sleep_for(1s);
}
C:\Temp>cl /EHsc /nologo /W4 /std:c++latest /MT /O2 meow.cpp
meow.cpp

C:\Temp>meow
200ns
100ns
0ns
0ns
0ns
0ns
100ns
0ns
[...]

I haven't fully analyzed this, but I observe that we still have some calls to GetTickCount64(), which IIRC uses the system clock instead of a steady clock:

STL/stl/src/atomic_wait.cpp

Lines 180 to 207 in 7841cf8

unsigned long long __stdcall __std_atomic_wait_get_deadline(const unsigned long long _Timeout) noexcept {
if (_Timeout == _Atomic_wait_no_deadline) {
return _Atomic_wait_no_deadline;
} else {
return GetTickCount64() + _Timeout;
}
}
unsigned long __stdcall __std_atomic_wait_get_remaining_timeout(unsigned long long _Deadline) noexcept {
static_assert(__std_atomic_wait_no_timeout == INFINITE,
"__std_atomic_wait_no_timeout is passed directly to underlying API, so should match it");
if (_Deadline == _Atomic_wait_no_deadline) {
return INFINITE;
}
const unsigned long long _Current_time = GetTickCount64();
if (_Current_time >= _Deadline) {
return 0;
}
unsigned long long _Remaining = _Deadline - _Current_time;
constexpr unsigned long _Ten_days = 864'000'000;
if (_Remaining > _Ten_days) {
return _Ten_days;
}
return static_cast<unsigned long>(_Remaining);
}

🛠️ Previous fixes for this category of bugs

The other uses of GetTickCount64() in sharedmutex.cpp appear to be properly "checked" in the headers against steady_clock or the user's clock. This is after the series of fixes:

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingchronoC++20 chronofixedSomething works now, yay!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions