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: c++03 10 // UNSUPPORTED: no-threads 11 12 // <mutex> 13 14 // class recursive_timed_mutex; 15 16 // void lock(); 17 18 #include <mutex> 19 #include <atomic> 20 #include <cassert> 21 #include <thread> 22 #include <vector> 23 24 #include "make_test_thread.h" 25 26 bool is_lockable(std::recursive_timed_mutex& m) { 27 bool did_lock; 28 std::thread t = support::make_test_thread([&] { 29 did_lock = m.try_lock(); 30 if (did_lock) 31 m.unlock(); // undo side effects 32 }); 33 t.join(); 34 35 return did_lock; 36 } 37 38 int main(int, char**) { 39 // Lock a mutex that is not locked yet. This should succeed. 40 { 41 std::recursive_timed_mutex m; 42 m.lock(); 43 m.unlock(); 44 } 45 46 // Lock a mutex that is already locked by this thread. This should succeed and the mutex should only 47 // be unlocked after a matching number of calls to unlock() on the same thread. 48 { 49 std::recursive_timed_mutex m; 50 int lock_count = 0; 51 for (int i = 0; i != 10; ++i) { 52 m.lock(); 53 ++lock_count; 54 } 55 while (lock_count != 0) { 56 assert(!is_lockable(m)); 57 m.unlock(); 58 --lock_count; 59 } 60 assert(is_lockable(m)); 61 } 62 63 // Lock a mutex that is already locked by another thread. This should block until it is unlocked. 64 { 65 std::atomic<bool> ready(false); 66 std::recursive_timed_mutex m; 67 m.lock(); 68 std::atomic<bool> is_locked_from_main(true); 69 70 std::thread t = support::make_test_thread([&] { 71 ready = true; 72 m.lock(); 73 assert(!is_locked_from_main); 74 m.unlock(); 75 }); 76 77 while (!ready) 78 /* spin */; 79 80 // We would rather signal this after we unlock, but that would create a race condition. 81 // We instead signal it before we unlock, which means that it's technically possible for 82 // the thread to take the lock while main is still holding it yet for the test to still pass. 83 is_locked_from_main = false; 84 m.unlock(); 85 86 t.join(); 87 } 88 89 // Make sure that at most one thread can acquire the mutex concurrently. 90 { 91 std::atomic<int> counter(0); 92 std::recursive_timed_mutex mutex; 93 94 std::vector<std::thread> threads; 95 for (int i = 0; i != 10; ++i) { 96 threads.push_back(support::make_test_thread([&] { 97 mutex.lock(); 98 counter++; 99 assert(counter == 1); 100 counter--; 101 mutex.unlock(); 102 })); 103 } 104 105 for (auto& t : threads) 106 t.join(); 107 } 108 109 return 0; 110 } 111