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, c++14 11 12 // <shared_mutex> 13 14 // class shared_mutex; 15 16 // void lock_shared(); 17 18 #include <shared_mutex> 19 #include <algorithm> 20 #include <atomic> 21 #include <cassert> 22 #include <thread> 23 #include <vector> 24 25 #include "make_test_thread.h" 26 27 int main(int, char**) { 28 // Lock-shared a mutex that is not locked yet. This should succeed. 29 { 30 std::shared_mutex m; 31 std::vector<std::thread> threads; 32 for (int i = 0; i != 5; ++i) { 33 threads.push_back(support::make_test_thread([&] { 34 m.lock_shared(); 35 m.unlock_shared(); 36 })); 37 } 38 39 for (auto& t : threads) 40 t.join(); 41 } 42 43 // Lock-shared a mutex that is already exclusively locked. This should block until it is unlocked. 44 { 45 std::atomic<int> ready(0); 46 std::shared_mutex m; 47 m.lock(); 48 std::atomic<bool> is_locked_from_main(true); 49 50 std::vector<std::thread> threads; 51 for (int i = 0; i != 5; ++i) { 52 threads.push_back(support::make_test_thread([&] { 53 ++ready; 54 while (ready < 5) 55 /* wait until all threads have been created */; 56 57 m.lock_shared(); 58 assert(!is_locked_from_main); 59 m.unlock_shared(); 60 })); 61 } 62 63 while (ready < 5) 64 /* wait until all threads have been created */; 65 66 // We would rather signal this after we unlock, but that would create a race condition. 67 // We instead signal it before we unlock, which means that it's technically possible for 68 // the thread to take the lock while we're still holding it and for the test to still pass. 69 is_locked_from_main = false; 70 m.unlock(); 71 72 for (auto& t : threads) 73 t.join(); 74 } 75 76 // Lock-shared a mutex that is already lock-shared. This should succeed. 77 { 78 std::atomic<int> ready(0); 79 std::shared_mutex m; 80 m.lock_shared(); 81 82 std::vector<std::thread> threads; 83 for (int i = 0; i != 5; ++i) { 84 threads.push_back(support::make_test_thread([&] { 85 ++ready; 86 while (ready < 5) 87 /* wait until all threads have been created */; 88 89 m.lock_shared(); 90 m.unlock_shared(); 91 })); 92 } 93 94 while (ready < 5) 95 /* wait until all threads have been created */; 96 97 m.unlock_shared(); 98 99 for (auto& t : threads) 100 t.join(); 101 } 102 103 // Create several threads that all acquire-shared the same mutex and make sure that each 104 // thread successfully acquires-shared the mutex. 105 // 106 // We record how many other threads were holding the mutex when it was acquired, which allows 107 // us to know whether the test was somewhat effective at causing multiple threads to lock at 108 // the same time. 109 { 110 std::shared_mutex mutex; 111 std::vector<std::thread> threads; 112 constexpr int n_threads = 5; 113 std::atomic<int> holders = 0; 114 int concurrent_holders[n_threads] = {}; 115 std::atomic<bool> ready = false; 116 117 for (int i = 0; i != n_threads; ++i) { 118 threads.push_back(support::make_test_thread([&, i] { 119 while (!ready) { 120 // spin 121 } 122 123 mutex.lock_shared(); 124 ++holders; 125 concurrent_holders[i] = holders; 126 127 mutex.unlock_shared(); 128 --holders; 129 })); 130 } 131 132 ready = true; // let the threads actually start shared-acquiring the mutex 133 for (auto& t : threads) 134 t.join(); 135 136 // We can't guarantee that we'll ever have more than 1 concurrent holder so that's what 137 // we assert, however in principle we should often trigger more than 1 concurrent holder. 138 int max_concurrent_holders = *std::max_element(std::begin(concurrent_holders), std::end(concurrent_holders)); 139 assert(max_concurrent_holders >= 1); 140 } 141 142 return 0; 143 } 144