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()

Reply via email to