Skip to content

Commit 14f7cfd

Browse files
committed
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 <mac@mcrowe.com> Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
1 parent 162ec53 commit 14f7cfd

File tree

1 file changed

+16
-13
lines changed
  • libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until

1 file changed

+16
-13
lines changed

libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
// { dg-do run { target c++14 } }
2+
// { dg-additional-options "-pthread" { target pthread } }
3+
// { dg-require-gthreads "" }
4+
// { dg-require-effective-target hosted }
25

36
#include <shared_mutex>
47
#include <chrono>
@@ -8,18 +11,26 @@
811

912
namespace chrono = std::chrono;
1013

11-
// thread.timedmutex.requirements.general:
14+
// [thread.timedmutex.requirements.general]:
1215
// If abs_time has already passed, the function attempts to obtain
1316
// ownership without blocking (as if by calling try_lock()).
1417

18+
// C++14 [thread.sharedtimedmutex.class] 3.2 says it's undefined for a thread
19+
// to attempt to recursively gain any ownership of a shared_timed_mutex.
20+
// This isn't just theoretical, as Glibc's pthread_rwlock_timedrdlock will
21+
// return EDEADLK if called on the same thread that already holds the
22+
// exclusive (write) lock.
23+
#define VERIFY_IN_NEW_THREAD(X) \
24+
(void) std::async(std::launch::async, [&] { VERIFY(X); })
25+
1526
template <typename Clock>
1627
void
1728
test_exclusive_absolute(chrono::nanoseconds offset)
1829
{
1930
std::shared_timed_mutex stm;
2031
chrono::time_point<Clock> tp(offset);
2132
VERIFY(stm.try_lock_until(tp));
22-
VERIFY(!stm.try_lock_until(tp));
33+
VERIFY_IN_NEW_THREAD(!stm.try_lock_until(tp));
2334
}
2435

2536
template <typename Clock>
@@ -32,15 +43,7 @@ test_shared_absolute(chrono::nanoseconds offset)
3243
stm.unlock_shared();
3344

3445
VERIFY(stm.try_lock_for(chrono::seconds{10}));
35-
36-
{
37-
// NPTL will give us EDEADLK if pthread_rwlock_timedrdlock() is called on
38-
// the same thread that already holds the exclusive (write) lock, so let's
39-
// arrange for a different thread to try to acquire the shared lock.
40-
auto t = std::async(std::launch::async, [&stm, tp]() {
41-
VERIFY(!stm.try_lock_shared_until(tp));
42-
});
43-
}
46+
VERIFY_IN_NEW_THREAD(!stm.try_lock_shared_until(tp));
4447
}
4548

4649
// The type of clock used for the actual wait depends on whether
@@ -53,7 +56,7 @@ test_exclusive_relative(chrono::nanoseconds offset)
5356
std::shared_timed_mutex stm;
5457
const auto d = -Clock::now().time_since_epoch() + offset;
5558
VERIFY(stm.try_lock_for(d));
56-
VERIFY(!stm.try_lock_for(d));
59+
VERIFY_IN_NEW_THREAD(!stm.try_lock_for(d));
5760
}
5861

5962
template <typename Clock>
@@ -66,7 +69,7 @@ test_shared_relative(chrono::nanoseconds offset)
6669
stm.unlock_shared();
6770
// Should complete immediately
6871
VERIFY(stm.try_lock_for(chrono::seconds{10}));
69-
VERIFY(!stm.try_lock_shared_for(d));
72+
VERIFY_IN_NEW_THREAD(!stm.try_lock_shared_for(d));
7073
}
7174

7275
int main()

0 commit comments

Comments
 (0)