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()49void 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 **)106int 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