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 // bool try_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 try_lock_shared_called = false;
31   bool unlock_shared_called   = false;
32 };
33 
34 struct TrackedMutex {
35   Monitor* monitor = nullptr;
36 
try_lock_sharedTrackedMutex37   bool try_lock_shared() {
38     if (monitor != nullptr)
39       monitor->try_lock_shared_called = true;
40     return true;
41   }
unlock_sharedTrackedMutex42   void unlock_shared() {
43     if (monitor != nullptr)
44       monitor->unlock_shared_called = true;
45   }
46 };
47 
48 template <class Mutex>
test()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         }
60 
61         std::shared_lock<Mutex> lock(mutex, std::defer_lock);
62         bool result = lock.try_lock();
63         assert(result);
64         assert(lock.owns_lock());
65       }));
66     }
67 
68     ready = true;
69     for (auto& t : threads)
70       t.join();
71   }
72 
73   // Make sure that we throw an exception if we try to re-lock a mutex that is
74   // already locked by the current thread.
75   {
76     Mutex mutex;
77 
78     std::shared_lock<Mutex> lock(mutex, std::defer_lock);
79     assert(lock.try_lock());
80     assert(lock.owns_lock());
81 #ifndef TEST_HAS_NO_EXCEPTIONS
82     try {
83       TEST_IGNORE_NODISCARD lock.try_lock();
84       assert(false);
85     } catch (std::system_error const& e) {
86       assert(e.code() == std::errc::resource_deadlock_would_occur);
87     }
88 #endif
89   }
90 
91   // Make sure that we throw an exception if we try to lock a shared_lock
92   // that is not associated to any mutex.
93   {
94     std::shared_lock<Mutex> lock; // not associated to a mutex
95 #ifndef TEST_HAS_NO_EXCEPTIONS
96     try {
97       TEST_IGNORE_NODISCARD lock.try_lock();
98       assert(false);
99     } catch (std::system_error const& e) {
100       assert(e.code() == std::errc::operation_not_permitted);
101     }
102 #endif
103   }
104 }
105 
main(int,char **)106 int main(int, char**) {
107 #if TEST_STD_VER >= 17
108   test<std::shared_mutex>();
109 #endif
110   test<std::shared_timed_mutex>();
111   test<TrackedMutex>();
112 
113   // Use shared_lock with a dummy mutex class that tracks whether each
114   // operation has been called or not.
115   {
116     Monitor monitor;
117     TrackedMutex mutex{&monitor};
118 
119     std::shared_lock<TrackedMutex> lock(mutex, std::defer_lock);
120     bool result = lock.try_lock();
121     assert(result);
122     assert(monitor.try_lock_shared_called);
123     assert(lock.owns_lock());
124 
125     lock.unlock();
126     assert(monitor.unlock_shared_called);
127   }
128   return 0;
129 }
130