1 //===----------------------------------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is dual licensed under the MIT and the University of Illinois Open 6 // Source Licenses. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 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 it's post 27 // conditions. This can happens 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 aquired 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 <thread> 60 #include <chrono> 61 #include <string> 62 #include <cstdlib> 63 #include <cassert> 64 65 #include "test_atomic.h" 66 67 void my_terminate() { 68 std::_Exit(0); // Use _Exit to prevent cleanup from taking place. 69 } 70 71 // The predicate used in the cv.wait calls. 72 bool pred = false; 73 bool pred_function() { 74 return pred == true; 75 } 76 77 class ThrowingMutex 78 { 79 AtomicBool locked; 80 unsigned state = 0; 81 ThrowingMutex(const ThrowingMutex&) = delete; 82 ThrowingMutex& operator=(const ThrowingMutex&) = delete; 83 public: 84 ThrowingMutex() = default; 85 ~ThrowingMutex() = default; 86 87 void lock() { 88 locked = true; 89 if (++state == 2) { 90 assert(pred); // Check that we actually waited until we were signaled. 91 throw 1; // this throw should end up calling terminate() 92 } 93 } 94 95 void unlock() { locked = false; } 96 bool isLocked() const { return locked == true; } 97 }; 98 99 ThrowingMutex mut; 100 std::condition_variable_any cv; 101 102 void signal_me() { 103 while (mut.isLocked()) {} // wait until T1 releases mut inside the cv.wait call. 104 pred = true; 105 cv.notify_one(); 106 } 107 108 typedef std::chrono::system_clock Clock; 109 typedef std::chrono::milliseconds MS; 110 111 int main(int argc, char** argv) { 112 assert(argc == 2); 113 int id = std::stoi(argv[1]); 114 assert(id >= 1 && id <= 6); 115 std::set_terminate(my_terminate); // set terminate after std::stoi because it can throw. 116 MS wait(250); 117 try { 118 mut.lock(); 119 assert(pred == false); 120 std::thread(signal_me).detach(); 121 switch (id) { 122 case 1: cv.wait(mut); break; 123 case 2: cv.wait(mut, pred_function); break; 124 case 3: cv.wait_for(mut, wait); break; 125 case 4: cv.wait_for(mut, wait, pred_function); break; 126 case 5: cv.wait_until(mut, Clock::now() + wait); break; 127 case 6: cv.wait_until(mut, Clock::now() + wait, pred_function); break; 128 default: assert(false); 129 } 130 } catch (...) {} 131 assert(false); 132 } 133