Passing a timeout from before the epoch to __platform_wait_until() currently results in an exception being thrown because futex(2) doesn't support negative timeouts. Let's just immediately return indicating a timeout has happened.
Add test cases to prove that this bug is fixed for std::binary_semaphore (which is just an alias for std::counting_semaphore<1>). The tests exercise cases that aren't problematic with the current code since system_clock is converted to steady_clock before calling __platform_wait_until() is called but they will protect against changes in the implementation reintroducing this bug. PR libstdc++/116586 libstdc++-v3/ChangeLog: * src/c++20/atomic.cc (__platform_wait_until): Return false on timeout before epoch. * testsuite/30_threads/semaphore/try_acquire_for.cc: Add tests. * testsuite/30_threads/semaphore/try_acquire_until.cc: Add tests. Signed-off-by: Mike Crowe <m...@mcrowe.com> --- libstdc++-v3/src/c++20/atomic.cc | 4 ++++ .../30_threads/semaphore/try_acquire_for.cc | 20 +++++++++++++++++ .../30_threads/semaphore/try_acquire_until.cc | 22 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/libstdc++-v3/src/c++20/atomic.cc b/libstdc++-v3/src/c++20/atomic.cc index 4120e1a0817..1e987213d80 100644 --- a/libstdc++-v3/src/c++20/atomic.cc +++ b/libstdc++-v3/src/c++20/atomic.cc @@ -359,6 +359,10 @@ __platform_wait_until(const __platform_wait_t* __addr, static_cast<long>(__ns.count()) }; + // SYS_futex returns EINVAL if timeout is negative, act as if we timed out immediately + if (__rt.tv_sec < 0 || __rt.tv_nsec < 0) + return false; + if (syscall (SYS_futex, __addr, static_cast<int>(__futex_wait_flags::__wait_bitset_private), __old, &__rt, nullptr, diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc index 39681c7ee56..fe0d704baf8 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -90,9 +90,29 @@ test03() s.try_acquire_for(timeout); } +// Prove semaphore doesn't suffer from PR116586 +template <typename Clock> +void +test_relative(std::chrono::nanoseconds offset) +{ + std::binary_semaphore sem(1); + VERIFY(sem.try_acquire_for(offset)); + VERIFY(!sem.try_acquire_for(offset)); +} + int main() { test01(); test02(); test03(); + for (const std::chrono::nanoseconds offset : { + std::chrono::nanoseconds{0}, + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds{-10}), + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds{-10}) + }) { + test_relative<std::chrono::system_clock>(offset); + test_relative<std::chrono::system_clock>(offset - std::chrono::system_clock::now().time_since_epoch()); + test_relative<std::chrono::steady_clock>(offset); + test_relative<std::chrono::steady_clock>(offset - std::chrono::steady_clock::now().time_since_epoch()); + } } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc index de0068d670a..a00f124b147 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -87,8 +87,30 @@ void test02() b.wait(1); } +// Prove semaphore doesn't suffer from PR116586 +template <typename Clock> +void +test_absolute(std::chrono::nanoseconds offset) +{ + std::binary_semaphore sem(1); + std::chrono::time_point<Clock> tp(offset); + VERIFY(sem.try_acquire_until(tp)); + VERIFY(!sem.try_acquire_until(tp)); +} + int main() { test01(); test02(); + for (const std::chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + std::chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds{-10}), + // tv_sec < 0 + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds{-10}) + }) { + test_absolute<std::chrono::system_clock>(offset); + test_absolute<std::chrono::steady_clock>(offset); + } } -- 2.39.5