1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: no-threads
10 // UNSUPPORTED: c++03, c++11
11 
12 // <shared_mutex>
13 
14 // template <class Mutex> class shared_lock;
15 
16 // void lock();
17 
18 #include <atomic>
19 #include <cassert>
20 #include <mutex> // std::defer_lock
21 #include <shared_mutex>
22 #include <system_error>
23 #include <thread>
24 #include <vector>
25 
26 #include "make_test_thread.h"
27 #include "test_macros.h"
28 
29 struct Monitor {
30   bool lock_shared_called   = false;
31   bool unlock_shared_called = false;
32 };
33 
34 struct TrackedMutex {
35   Monitor* monitor = nullptr;
36 
lock_sharedTrackedMutex37   void lock_shared() {
38     if (monitor != nullptr)
39       monitor->lock_shared_called = true;
40   }
unlock_sharedTrackedMutex41   void unlock_shared() {
42     if (monitor != nullptr)
43       monitor->unlock_shared_called = true;
44   }
45 };
46 
47 template <class Mutex>
test()48 void test() {
49   // Basic sanity test
50   {
51     Mutex mutex;
52     std::vector<std::thread> threads;
53     std::atomic<bool> ready(false);
54     for (int i = 0; i != 5; ++i) {
55       threads.push_back(support::make_test_thread([&] {
56         while (!ready) {
57           // spin
58         }
59 
60         std::shared_lock<Mutex> lock(mutex, std::defer_lock);
61         lock.lock();
62         assert(lock.owns_lock());
63       }));
64     }
65 
66     ready = true;
67     for (auto& t : threads)
68       t.join();
69   }
70 
71   // Try locking the same shared_lock again in the same thread. This should throw an exception.
72   {
73     Mutex mutex;
74     std::shared_lock<Mutex> lock(mutex, std::defer_lock);
75     lock.lock();
76     assert(lock.owns_lock());
77 #ifndef TEST_HAS_NO_EXCEPTIONS
78     try {
79       lock.lock();
80       assert(false);
81     } catch (std::system_error const& e) {
82       assert(e.code() == std::errc::resource_deadlock_would_occur);
83     }
84 #endif
85   }
86 
87   // Try locking a shared_lock that isn't associated to any mutex. This should throw an exception.
88   {
89     std::shared_lock<Mutex> lock; // no associated mutex
90 #ifndef TEST_HAS_NO_EXCEPTIONS
91     try {
92       lock.lock();
93       assert(false);
94     } catch (std::system_error const& e) {
95       assert(e.code() == std::errc::operation_not_permitted);
96     }
97 #endif
98   }
99 }
100 
main(int,char **)101 int main(int, char**) {
102 #if TEST_STD_VER >= 17
103   test<std::shared_mutex>();
104 #endif
105   test<std::shared_timed_mutex>();
106   test<TrackedMutex>();
107 
108   // Use shared_lock with a dummy mutex class that tracks whether each
109   // operation has been called or not.
110   {
111     Monitor monitor;
112     TrackedMutex mutex{&monitor};
113 
114     std::shared_lock<TrackedMutex> lock(mutex, std::defer_lock);
115     lock.lock();
116     assert(monitor.lock_shared_called);
117     assert(lock.owns_lock());
118 
119     lock.unlock();
120     assert(monitor.unlock_shared_called);
121   }
122 
123   return 0;
124 }
125