https://gcc.gnu.org/g:14f7cfd292f18d9f83a9d4f36338b2399ad79a59
commit r16-4697-g14f7cfd292f18d9f83a9d4f36338b2399ad79a59 Author: Jonathan Wakely <[email protected]> Date: Fri Oct 24 11:38:22 2025 +0100 libstdc++: Fix deadlock in shared_timed_mutex test [PR122401] The test_shared_relative function deadlocks on older Glibc versions that don't have pthread_rwlock_clockrdlock, because (as already mentioned earlier in the test file) pthread_rwlock_timedrdlock returns EDEADLK if the thread that already holds a write lock attempts to acquire read lock, causing std::shared_timed_mutex to loop forever. The fix is to do the invalid try_lock_shared_for call on a different thread. To avoid undefined behaviour, we need to make the same changes to all calls that try to acquire a lock that is already held. Also add missing -pthread for PR122401. libstdc++-v3/ChangeLog: PR libstdc++/122401 * testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc: Do not try to acquire locks on the thread that already holds a lock. Add -pthread for et pthread. Reviewed-by: Mike Crowe <[email protected]> Reviewed-by: Tomasz KamiĆski <[email protected]> Diff: --- .../shared_timed_mutex/try_lock_until/116586.cc | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) 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 index cebbb3a258d9..5736b7dc0470 100644 --- 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 @@ -1,4 +1,7 @@ // { dg-do run { target c++14 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } #include <shared_mutex> #include <chrono> @@ -8,10 +11,18 @@ namespace chrono = std::chrono; -// thread.timedmutex.requirements.general: +// [thread.timedmutex.requirements.general]: // If abs_time has already passed, the function attempts to obtain // ownership without blocking (as if by calling try_lock()). +// C++14 [thread.sharedtimedmutex.class] 3.2 says it's undefined for a thread +// to attempt to recursively gain any ownership of a shared_timed_mutex. +// This isn't just theoretical, as Glibc's pthread_rwlock_timedrdlock will +// return EDEADLK if called on the same thread that already holds the +// exclusive (write) lock. +#define VERIFY_IN_NEW_THREAD(X) \ + (void) std::async(std::launch::async, [&] { VERIFY(X); }) + template <typename Clock> void test_exclusive_absolute(chrono::nanoseconds offset) @@ -19,7 +30,7 @@ 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)); + VERIFY_IN_NEW_THREAD(!stm.try_lock_until(tp)); } template <typename Clock> @@ -32,15 +43,7 @@ test_shared_absolute(chrono::nanoseconds offset) 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)); - }); - } + VERIFY_IN_NEW_THREAD(!stm.try_lock_shared_until(tp)); } // The type of clock used for the actual wait depends on whether @@ -53,7 +56,7 @@ 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)); + VERIFY_IN_NEW_THREAD(!stm.try_lock_for(d)); } template <typename Clock> @@ -66,7 +69,7 @@ test_shared_relative(chrono::nanoseconds offset) stm.unlock_shared(); // Should complete immediately VERIFY(stm.try_lock_for(chrono::seconds{10})); - VERIFY(!stm.try_lock_shared_for(d)); + VERIFY_IN_NEW_THREAD(!stm.try_lock_shared_for(d)); } int main()
