Skip to content

Commit 29ef92b

Browse files
authored
[libc++] Refactor tests for shared_mutex and shared_timed_mutex (#100783)
This makes the tests less flaky and also makes a few other refactorings like using traits instead of .compile.fail.cpp tests.
1 parent d8b985c commit 29ef92b

File tree

18 files changed

+900
-624
lines changed

18 files changed

+900
-624
lines changed
Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8-
//
8+
99
// UNSUPPORTED: no-threads
1010
// UNSUPPORTED: c++03, c++11, c++14
1111

@@ -16,12 +16,6 @@
1616
// shared_mutex& operator=(const shared_mutex&) = delete;
1717

1818
#include <shared_mutex>
19+
#include <type_traits>
1920

20-
int main(int, char**)
21-
{
22-
std::shared_mutex m0;
23-
std::shared_mutex m1;
24-
m1 = m0; // expected-error {{overload resolution selected deleted operator '='}}
25-
26-
return 0;
27-
}
21+
static_assert(!std::is_copy_assignable<std::shared_mutex>::value, "");
Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8-
//
8+
99
// UNSUPPORTED: no-threads
1010
// UNSUPPORTED: c++03, c++11, c++14
1111

@@ -16,11 +16,6 @@
1616
// shared_mutex(const shared_mutex&) = delete;
1717

1818
#include <shared_mutex>
19+
#include <type_traits>
1920

20-
int main(int, char**)
21-
{
22-
std::shared_mutex m0;
23-
std::shared_mutex m1(m0); // expected-error {{call to deleted constructor of 'std::shared_mutex'}}
24-
25-
return 0;
26-
}
21+
static_assert(!std::is_copy_constructible<std::shared_mutex>::value, "");
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8-
//
8+
99
// UNSUPPORTED: no-threads
1010
// UNSUPPORTED: c++03, c++11, c++14
1111

@@ -19,10 +19,9 @@
1919

2020
#include "test_macros.h"
2121

22-
int main(int, char**)
23-
{
24-
std::shared_mutex m;
25-
(void)m;
22+
int main(int, char**) {
23+
std::shared_mutex m;
24+
(void)m;
2625

27-
return 0;
26+
return 0;
2827
}

libcxx/test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,105 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8-
//
8+
99
// UNSUPPORTED: no-threads
1010
// UNSUPPORTED: c++03, c++11, c++14
1111

12-
// ALLOW_RETRIES: 2
13-
1412
// <shared_mutex>
1513

1614
// class shared_mutex;
1715

1816
// void lock();
1917

18+
#include <shared_mutex>
19+
#include <atomic>
2020
#include <cassert>
2121
#include <chrono>
22-
#include <cstdlib>
23-
#include <shared_mutex>
2422
#include <thread>
23+
#include <vector>
2524

2625
#include "make_test_thread.h"
2726
#include "test_macros.h"
2827

29-
std::shared_mutex m;
28+
int main(int, char**) {
29+
// Exclusive-lock a mutex that is not locked yet. This should succeed.
30+
{
31+
std::shared_mutex m;
32+
m.lock();
33+
m.unlock();
34+
}
3035

31-
typedef std::chrono::system_clock Clock;
32-
typedef Clock::time_point time_point;
33-
typedef Clock::duration duration;
34-
typedef std::chrono::milliseconds ms;
35-
typedef std::chrono::nanoseconds ns;
36+
// Exclusive-lock a mutex that is already locked exclusively. This should block until it is unlocked.
37+
{
38+
std::atomic<bool> ready(false);
39+
std::shared_mutex m;
40+
m.lock();
41+
std::atomic<bool> is_locked_from_main(true);
3642

37-
ms WaitTime = ms(250);
43+
std::thread t = support::make_test_thread([&] {
44+
ready = true;
45+
m.lock();
46+
assert(!is_locked_from_main);
47+
m.unlock();
48+
});
3849

39-
// Thread sanitizer causes more overhead and will sometimes cause this test
40-
// to fail. To prevent this we give Thread sanitizer more time to complete the
41-
// test.
42-
#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
43-
ms Tolerance = ms(50);
44-
#else
45-
ms Tolerance = ms(50 * 5);
46-
#endif
50+
while (!ready)
51+
/* spin */;
4752

48-
void f()
49-
{
50-
time_point t0 = Clock::now();
51-
m.lock();
52-
time_point t1 = Clock::now();
53+
// We would rather signal this after we unlock, but that would create a race condition.
54+
// We instead signal it before we unlock, which means that it's technically possible for the thread
55+
// to take the lock while we're still holding it and for the test to still pass.
56+
is_locked_from_main = false;
5357
m.unlock();
54-
ns d = t1 - t0 - WaitTime;
55-
assert(d < Tolerance); // within tolerance
56-
}
5758

58-
int main(int, char**)
59-
{
60-
m.lock();
61-
std::thread t = support::make_test_thread(f);
62-
std::this_thread::sleep_for(WaitTime);
63-
m.unlock();
6459
t.join();
60+
}
61+
62+
// Exclusive-lock a mutex that is already share-locked. This should block until it is unlocked.
63+
{
64+
std::atomic<bool> ready(false);
65+
std::shared_mutex m;
66+
m.lock_shared();
67+
std::atomic<bool> is_locked_from_main(true);
68+
69+
std::thread t = support::make_test_thread([&] {
70+
ready = true;
71+
m.lock();
72+
assert(!is_locked_from_main);
73+
m.unlock();
74+
});
75+
76+
while (!ready)
77+
/* spin */;
78+
79+
// We would rather signal this after we unlock, but that would create a race condition.
80+
// We instead signal it before we unlock, which means that it's technically possible for
81+
// the thread to take the lock while we're still holding it and for the test to still pass.
82+
is_locked_from_main = false;
83+
m.unlock_shared();
84+
85+
t.join();
86+
}
87+
88+
// Make sure that at most one thread can acquire the mutex concurrently.
89+
{
90+
std::atomic<int> counter = 0;
91+
std::shared_mutex mutex;
92+
93+
std::vector<std::thread> threads;
94+
for (int i = 0; i != 10; ++i) {
95+
threads.push_back(support::make_test_thread([&] {
96+
mutex.lock();
97+
counter++;
98+
assert(counter == 1);
99+
counter--;
100+
mutex.unlock();
101+
}));
102+
}
103+
104+
for (auto& t : threads)
105+
t.join();
106+
}
65107

66108
return 0;
67109
}

0 commit comments

Comments
 (0)