Skip to content

Commit 12dd2c7

Browse files
committed
[libc++] Refactor flaky tests for std::shared_lock
This makes the tests non-flaky.
1 parent 9232591 commit 12dd2c7

File tree

3 files changed

+240
-165
lines changed

3 files changed

+240
-165
lines changed

libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.cons/mutex.pass.cpp

Lines changed: 65 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8-
//
8+
99
// UNSUPPORTED: no-threads
1010
// UNSUPPORTED: c++03, c++11
11-
// ALLOW_RETRIES: 2
1211

1312
// <shared_mutex>
1413

@@ -19,87 +18,86 @@
1918
// template<class _Mutex> shared_lock(shared_lock<_Mutex>)
2019
// -> shared_lock<_Mutex>; // C++17
2120

21+
#include <atomic>
2222
#include <cassert>
23-
#include <chrono>
24-
#include <cstdlib>
2523
#include <shared_mutex>
2624
#include <thread>
2725
#include <vector>
2826

2927
#include "make_test_thread.h"
3028
#include "test_macros.h"
3129

32-
typedef std::chrono::system_clock Clock;
33-
typedef Clock::time_point time_point;
34-
typedef Clock::duration duration;
35-
typedef std::chrono::milliseconds ms;
36-
typedef std::chrono::nanoseconds ns;
37-
38-
ms WaitTime = ms(250);
39-
40-
// Thread sanitizer causes more overhead and will sometimes cause this test
41-
// to fail. To prevent this we give Thread sanitizer more time to complete the
42-
// test.
43-
#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
44-
ms Tolerance = ms(50);
45-
#else
46-
ms Tolerance = ms(50 * 5);
47-
#endif
30+
struct Monitor {
31+
bool lock_shared_called = false;
32+
bool unlock_shared_called = false;
33+
};
4834

49-
std::shared_timed_mutex m;
35+
struct TrackedMutex {
36+
Monitor* monitor = nullptr;
5037

51-
void f()
52-
{
53-
time_point t0 = Clock::now();
54-
time_point t1;
55-
{
56-
std::shared_lock<std::shared_timed_mutex> ul(m);
57-
t1 = Clock::now();
58-
}
59-
ns d = t1 - t0 - WaitTime;
60-
assert(d < Tolerance); // within tolerance
61-
}
38+
void lock_shared() {
39+
if (monitor != nullptr)
40+
monitor->lock_shared_called = true;
41+
}
42+
void unlock_shared() {
43+
if (monitor != nullptr)
44+
monitor->unlock_shared_called = true;
45+
}
46+
};
6247

63-
void g()
64-
{
65-
time_point t0 = Clock::now();
66-
time_point t1;
67-
{
68-
std::shared_lock<std::shared_timed_mutex> ul(m);
69-
t1 = Clock::now();
70-
}
71-
ns d = t1 - t0;
72-
assert(d < Tolerance); // within tolerance
73-
}
48+
template <class Mutex>
49+
void test() {
50+
// Basic sanity test
51+
{
52+
Mutex mutex;
53+
std::vector<std::thread> threads;
54+
std::atomic<bool> ready(false);
55+
for (int i = 0; i != 5; ++i) {
56+
threads.push_back(support::make_test_thread([&] {
57+
while (!ready) {
58+
// spin
59+
}
7460

75-
int main(int, char**)
76-
{
77-
std::vector<std::thread> v;
78-
{
79-
m.lock();
80-
for (int i = 0; i < 5; ++i)
81-
v.push_back(support::make_test_thread(f));
82-
std::this_thread::sleep_for(WaitTime);
83-
m.unlock();
84-
for (auto& t : v)
85-
t.join();
86-
}
87-
{
88-
m.lock_shared();
89-
for (auto& t : v)
90-
t = support::make_test_thread(g);
91-
std::thread q = support::make_test_thread(f);
92-
std::this_thread::sleep_for(WaitTime);
93-
m.unlock_shared();
94-
for (auto& t : v)
95-
t.join();
96-
q.join();
61+
std::shared_lock<Mutex> lock(mutex);
62+
assert(lock.owns_lock());
63+
}));
9764
}
9865

66+
ready = true;
67+
for (auto& t : threads)
68+
t.join();
69+
}
70+
71+
// Test CTAD
72+
{
73+
#if TEST_STD_VER >= 17
74+
Mutex mutex;
75+
std::shared_lock lock(mutex);
76+
static_assert(std::is_same<decltype(lock), std::shared_lock<Mutex>>::value);
77+
#endif
78+
}
79+
}
80+
81+
int main(int, char**) {
9982
#if TEST_STD_VER >= 17
100-
std::shared_lock sl(m);
101-
static_assert((std::is_same<decltype(sl), std::shared_lock<decltype(m)>>::value), "" );
83+
test<std::shared_mutex>();
10284
#endif
85+
test<std::shared_timed_mutex>();
86+
test<TrackedMutex>();
87+
88+
// Use shared_lock with a dummy mutex class that tracks whether each
89+
// operation has been called or not.
90+
{
91+
Monitor monitor;
92+
TrackedMutex mutex{&monitor};
93+
94+
std::shared_lock<TrackedMutex> lock(mutex);
95+
assert(monitor.lock_shared_called);
96+
assert(lock.owns_lock());
97+
98+
lock.unlock();
99+
assert(monitor.unlock_shared_called);
100+
}
103101

104102
return 0;
105103
}

libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.locking/lock.pass.cpp

Lines changed: 82 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,18 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8-
//
8+
99
// UNSUPPORTED: no-threads
1010
// UNSUPPORTED: c++03, c++11
11-
// ALLOW_RETRIES: 2
1211

1312
// <shared_mutex>
1413

1514
// template <class Mutex> class shared_lock;
1615

1716
// void lock();
1817

18+
#include <atomic>
1919
#include <cassert>
20-
#include <chrono>
21-
#include <cstdlib>
22-
#include <mutex>
2320
#include <shared_mutex>
2421
#include <system_error>
2522
#include <thread>
@@ -28,71 +25,99 @@
2825
#include "make_test_thread.h"
2926
#include "test_macros.h"
3027

31-
std::shared_timed_mutex m;
28+
struct Monitor {
29+
bool lock_shared_called = false;
30+
bool unlock_shared_called = false;
31+
};
3232

33-
typedef std::chrono::system_clock Clock;
34-
typedef Clock::time_point time_point;
35-
typedef Clock::duration duration;
36-
typedef std::chrono::milliseconds ms;
37-
typedef std::chrono::nanoseconds ns;
33+
struct TrackedMutex {
34+
Monitor* monitor = nullptr;
3835

39-
ms WaitTime = ms(250);
36+
void lock_shared() {
37+
if (monitor != nullptr)
38+
monitor->lock_shared_called = true;
39+
}
40+
void unlock_shared() {
41+
if (monitor != nullptr)
42+
monitor->unlock_shared_called = true;
43+
}
44+
};
4045

41-
// Thread sanitizer causes more overhead and will sometimes cause this test
42-
// to fail. To prevent this we give Thread sanitizer more time to complete the
43-
// test.
44-
#if !defined(TEST_IS_EXECUTED_IN_A_SLOW_ENVIRONMENT)
45-
ms Tolerance = ms(25);
46-
#else
47-
ms Tolerance = ms(25 * 5);
48-
#endif
46+
template <class Mutex>
47+
void test() {
48+
// Basic sanity test
49+
{
50+
Mutex mutex;
51+
std::vector<std::thread> threads;
52+
std::atomic<bool> ready(false);
53+
for (int i = 0; i != 5; ++i) {
54+
threads.push_back(support::make_test_thread([&] {
55+
while (!ready) {
56+
// spin
57+
}
4958

59+
std::shared_lock<Mutex> lock(mutex, std::defer_lock);
60+
lock.lock();
61+
assert(lock.owns_lock());
62+
}));
63+
}
64+
65+
ready = true;
66+
for (auto& t : threads)
67+
t.join();
68+
}
5069

51-
void f()
52-
{
53-
std::shared_lock<std::shared_timed_mutex> lk(m, std::defer_lock);
54-
time_point t0 = Clock::now();
55-
lk.lock();
56-
time_point t1 = Clock::now();
57-
assert(lk.owns_lock() == true);
58-
ns d = t1 - t0 - WaitTime;
59-
assert(d < Tolerance); // within tolerance
70+
// Try locking the same shared_lock again in the same thread. This should throw an exception.
71+
{
72+
Mutex mutex;
73+
std::shared_lock<Mutex> lock(mutex, std::defer_lock);
74+
lock.lock();
75+
assert(lock.owns_lock());
6076
#ifndef TEST_HAS_NO_EXCEPTIONS
61-
try
62-
{
63-
lk.lock();
64-
assert(false);
65-
}
66-
catch (std::system_error& e)
67-
{
68-
assert(e.code().value() == EDEADLK);
77+
try {
78+
lock.lock();
79+
assert(false);
80+
} catch (std::system_error const& e) {
81+
assert(e.code() == std::errc::resource_deadlock_would_occur);
6982
}
7083
#endif
71-
lk.unlock();
72-
lk.release();
84+
}
85+
86+
// Try locking a shared_lock that isn't associated to any mutex. This should throw an exception.
87+
{
88+
std::shared_lock<Mutex> lock; // no associated mutex
7389
#ifndef TEST_HAS_NO_EXCEPTIONS
74-
try
75-
{
76-
lk.lock();
77-
assert(false);
78-
}
79-
catch (std::system_error& e)
80-
{
81-
assert(e.code().value() == EPERM);
90+
try {
91+
lock.lock();
92+
assert(false);
93+
} catch (std::system_error const& e) {
94+
assert(e.code() == std::errc::operation_not_permitted);
8295
}
8396
#endif
97+
}
8498
}
8599

86-
int main(int, char**)
87-
{
88-
m.lock();
89-
std::vector<std::thread> v;
90-
for (int i = 0; i < 5; ++i)
91-
v.push_back(support::make_test_thread(f));
92-
std::this_thread::sleep_for(WaitTime);
93-
m.unlock();
94-
for (auto& t : v)
95-
t.join();
100+
int main(int, char**) {
101+
#if TEST_STD_VER >= 17
102+
test<std::shared_mutex>();
103+
#endif
104+
test<std::shared_timed_mutex>();
105+
test<TrackedMutex>();
106+
107+
// Use shared_lock with a dummy mutex class that tracks whether each
108+
// operation has been called or not.
109+
{
110+
Monitor monitor;
111+
TrackedMutex mutex{&monitor};
112+
113+
std::shared_lock<TrackedMutex> lock(mutex, std::defer_lock);
114+
lock.lock();
115+
assert(monitor.lock_shared_called);
116+
assert(lock.owns_lock());
117+
118+
lock.unlock();
119+
assert(monitor.unlock_shared_called);
120+
}
96121

97122
return 0;
98123
}

0 commit comments

Comments
 (0)