https://gcc.gnu.org/g:fb558b78108ff2974248190bb64b4c19215535bc
commit r16-4427-gfb558b78108ff2974248190bb64b4c19215535bc Author: Jonathan Wakely <[email protected]> Date: Thu Oct 9 11:11:44 2025 +0100 libstdc++: Add std::shared_timed_mutex tests for negative timeouts [PR116586] Add tests to show that std::shared_timed_mutex correctly handles negative timeouts. libstdc++-v3/ChangeLog: PR libstdc++/116586 * testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc: New test. Signed-off-by: Mike Crowe <[email protected]> Diff: --- .../shared_timed_mutex/try_lock_until/116586.cc | 97 ++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc b/libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc new file mode 100644 index 000000000000..cebbb3a258d9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc @@ -0,0 +1,97 @@ +// { dg-do run { target c++14 } } + +#include <shared_mutex> +#include <chrono> +#include <future> +#include <initializer_list> +#include <testsuite_hooks.h> + +namespace chrono = std::chrono; + +// thread.timedmutex.requirements.general: +// If abs_time has already passed, the function attempts to obtain +// ownership without blocking (as if by calling try_lock()). + +template <typename Clock> +void +test_exclusive_absolute(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + chrono::time_point<Clock> tp(offset); + VERIFY(stm.try_lock_until(tp)); + VERIFY(!stm.try_lock_until(tp)); +} + +template <typename Clock> +void +test_shared_absolute(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + chrono::time_point<Clock> tp(offset); + VERIFY(stm.try_lock_shared_until(tp)); + stm.unlock_shared(); + + VERIFY(stm.try_lock_for(chrono::seconds{10})); + + { + // NPTL will give us EDEADLK if pthread_rwlock_timedrdlock() is called on + // the same thread that already holds the exclusive (write) lock, so let's + // arrange for a different thread to try to acquire the shared lock. + auto t = std::async(std::launch::async, [&stm, tp]() { + VERIFY(!stm.try_lock_shared_until(tp)); + }); + } +} + +// The type of clock used for the actual wait depends on whether +// _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK is defined. We might as well just test +// both steady_clock and system_clock. +template <typename Clock> +void +test_exclusive_relative(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(stm.try_lock_for(d)); + VERIFY(!stm.try_lock_for(d)); +} + +template <typename Clock> +void +test_shared_relative(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(stm.try_lock_shared_for(d)); + stm.unlock_shared(); + // Should complete immediately + VERIFY(stm.try_lock_for(chrono::seconds{10})); + VERIFY(!stm.try_lock_shared_for(d)); +} + +int main() +{ + // Try once with an offset that ought to result in tv_sec == 0, tv_nsec < 0 + // and one with an offset that ought to result in tv_sec < 0, tv_nsec == 0 + // for the absolute calls at least. It's not really possible to arrange for + // the relative calls to have tv_nsec == 0 due to time advancing. + using namespace std::chrono_literals; + for (const chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + chrono::nanoseconds{-10ms}, + // tv_sec < 0 + chrono::nanoseconds{-10s} + }) { + test_exclusive_absolute<chrono::system_clock>(offset); + test_shared_absolute<chrono::system_clock>(offset); + test_exclusive_relative<chrono::system_clock>(offset); + test_shared_relative<chrono::system_clock>(offset); + + test_exclusive_absolute<chrono::steady_clock>(offset); + test_shared_absolute<chrono::steady_clock>(offset); + test_exclusive_relative<chrono::steady_clock>(offset); + test_shared_relative<chrono::steady_clock>(offset); + } +}
