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