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-exceptions 10 // UNSUPPORTED: no-threads 11 12 // <condition_variable> 13 14 // class condition_variable_any; 15 16 // RUN: %{build} 17 // RUN: %{run} 1 18 // RUN: %{run} 2 19 // RUN: %{run} 3 20 // RUN: %{run} 4 21 // RUN: %{run} 5 22 // RUN: %{run} 6 23 // RUN: %{run} 7 24 // RUN: %{run} 8 25 // RUN: %{run} 9 26 27 // ----------------------------------------------------------------------------- 28 // Overview 29 // Check that std::terminate is called if wait(...) fails to meet its post 30 // conditions. This can happen when reacquiring the mutex throws 31 // an exception. 32 // 33 // The following methods are tested within this file 34 // 1. void wait(Lock& lock); 35 // 2. void wait(Lock& lock, Pred); 36 // 3. void wait_for(Lock& lock, Duration); 37 // 4. void wait_for(Lock& lock, Duration, Pred); 38 // 5. void wait_until(Lock& lock, TimePoint); 39 // 6. void wait_until(Lock& lock, TimePoint, Pred); 40 // 7. bool wait(Lock& lock, stop_token stoken, Predicate pred); 41 // 8. bool wait_for(Lock& lock, stop_token stoken, Duration, Predicate pred); 42 // 9. bool wait_until(Lock& lock, stop_token stoken, TimePoint, Predicate pred); 43 // 44 // Plan 45 // 1 Create a mutex type, 'ThrowingMutex', that throws when the lock is acquired 46 // for the *second* time. 47 // 48 // 2 Replace the terminate handler with one that exits with a '0' exit code. 49 // 50 // 3 Create a 'condition_variable_any' object 'cv' and a 'ThrowingMutex' 51 // object 'm' and lock 'm'. 52 // 53 // 4 Start a thread 'T2' that will notify 'cv' once 'm' has been unlocked. 54 // 55 // 5 From the main thread call the specified wait method on 'cv' with 'm'. 56 // When 'T2' notifies 'cv' and the wait method attempts to re-lock 57 // 'm' an exception will be thrown from 'm.lock()'. 58 // 59 // 6 Check that control flow does not return from the wait method and that 60 // terminate is called (If the program exits with a 0 exit code we know 61 // that terminate has been called) 62 63 64 #include <atomic> 65 #include <cassert> 66 #include <chrono> 67 #include <condition_variable> 68 #include <cstdlib> 69 #include <exception> 70 #include <string> 71 #include <stop_token> 72 #include <thread> 73 74 #include "make_test_thread.h" 75 #include "test_macros.h" 76 77 void my_terminate() { 78 std::_Exit(0); // Use _Exit to prevent cleanup from taking place. 79 } 80 81 // The predicate used in the cv.wait calls. 82 bool pred = false; 83 bool pred_function() { 84 return pred == true; 85 } 86 87 class ThrowingMutex 88 { 89 std::atomic_bool locked; 90 unsigned state = 0; 91 ThrowingMutex(const ThrowingMutex&) = delete; 92 ThrowingMutex& operator=(const ThrowingMutex&) = delete; 93 public: 94 ThrowingMutex() { 95 locked = false; 96 } 97 ~ThrowingMutex() = default; 98 99 void lock() { 100 locked = true; 101 if (++state == 2) { 102 assert(pred); // Check that we actually waited until we were signaled. 103 throw 1; // this throw should end up calling terminate() 104 } 105 } 106 107 void unlock() { locked = false; } 108 bool isLocked() const { return locked == true; } 109 }; 110 111 ThrowingMutex mut; 112 std::condition_variable_any cv; 113 114 void signal_me() { 115 while (mut.isLocked()) {} // wait until T1 releases mut inside the cv.wait call. 116 pred = true; 117 cv.notify_one(); 118 } 119 120 typedef std::chrono::system_clock Clock; 121 typedef std::chrono::milliseconds MS; 122 123 int main(int argc, char **argv) { 124 assert(argc == 2); 125 int id = std::stoi(argv[1]); 126 assert(id >= 1 && id <= 9); 127 std::set_terminate(my_terminate); // set terminate after std::stoi because it can throw. 128 MS wait(250); 129 try { 130 mut.lock(); 131 assert(pred == false); 132 support::make_test_thread(signal_me).detach(); 133 switch (id) { 134 case 1: cv.wait(mut); break; 135 case 2: cv.wait(mut, pred_function); break; 136 case 3: cv.wait_for(mut, wait); break; 137 case 4: cv.wait_for(mut, wait, pred_function); break; 138 case 5: cv.wait_until(mut, Clock::now() + wait); break; 139 case 6: cv.wait_until(mut, Clock::now() + wait, pred_function); break; 140 #if TEST_STD_VER >= 20 && !(defined(_LIBCPP_VERSION) && !_LIBCPP_AVAILABILITY_HAS_SYNC) 141 case 7: cv.wait(mut, std::stop_source{}.get_token(), pred_function); break; 142 case 8: cv.wait_for(mut, std::stop_source{}.get_token(), wait, pred_function); break; 143 case 9: cv.wait_until(mut, std::stop_source{}.get_token(), Clock::now() + wait, pred_function); break; 144 #else 145 case 7: 146 case 8: 147 case 9: 148 return 0; 149 #endif 150 default: assert(false); 151 } 152 } catch (...) {} 153 assert(false); 154 155 return 0; 156 } 157