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 // class shared_timed_mutex; 15 16 // template <class Rep, class Period> 17 // bool try_lock_for(const chrono::duration<Rep, Period>& rel_time); 18 19 #include <shared_mutex> 20 #include <atomic> 21 #include <cassert> 22 #include <chrono> 23 #include <thread> 24 25 #include "make_test_thread.h" 26 27 template <class Function> 28 std::chrono::microseconds measure(Function f) { 29 std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); 30 f(); 31 std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); 32 return std::chrono::duration_cast<std::chrono::microseconds>(end - start); 33 } 34 35 int main(int, char**) { 36 // Try to lock a mutex that is not locked yet. This should succeed immediately. 37 { 38 std::shared_timed_mutex m; 39 bool succeeded = m.try_lock_for(std::chrono::milliseconds(1)); 40 assert(succeeded); 41 m.unlock(); 42 } 43 44 // Try to lock an already-locked mutex for a long enough amount of time and succeed. 45 // This is technically flaky, but we use such long durations that it should pass even 46 // in slow or contended environments. 47 { 48 std::chrono::milliseconds const wait_time(500); 49 std::chrono::milliseconds const tolerance = wait_time * 3; 50 std::atomic<bool> ready(false); 51 52 std::shared_timed_mutex m; 53 m.lock(); 54 55 std::thread t = support::make_test_thread([&] { 56 auto elapsed = measure([&] { 57 ready = true; 58 bool succeeded = m.try_lock_for(wait_time); 59 assert(succeeded); 60 m.unlock(); 61 }); 62 63 // Ensure we didn't wait significantly longer than our timeout. This is technically 64 // flaky and non-conforming because an implementation is free to block for arbitrarily 65 // long, but any decent quality implementation should pass this test. 66 assert(elapsed - wait_time < tolerance); 67 }); 68 69 // Wait for the thread to be ready to take the lock before we unlock it from here, otherwise 70 // there's a high chance that we're not testing the "locking an already locked" mutex use case. 71 // There is still technically a race condition here. 72 while (!ready) 73 /* spin */; 74 std::this_thread::sleep_for(wait_time / 5); 75 76 m.unlock(); // this should allow the thread to lock 'm' 77 t.join(); 78 } 79 80 // Try to lock an already-locked mutex for a short amount of time and fail. 81 // Again, this is technically flaky but we use such long durations that it should work. 82 { 83 std::chrono::milliseconds const wait_time(10); 84 std::chrono::milliseconds const tolerance(750); // in case the thread we spawned goes to sleep or something 85 86 std::shared_timed_mutex m; 87 m.lock(); 88 89 std::thread t = support::make_test_thread([&] { 90 auto elapsed = measure([&] { 91 bool succeeded = m.try_lock_for(wait_time); 92 assert(!succeeded); 93 }); 94 95 // Ensure we failed within some bounded time. 96 assert(elapsed - wait_time < tolerance); 97 }); 98 99 t.join(); 100 101 m.unlock(); 102 } 103 104 return 0; 105 } 106