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 #include "make_test_thread.h" 67 68 void my_terminate() { 69 std::_Exit(0); // Use _Exit to prevent cleanup from taking place. 70 } 71 72 // The predicate used in the cv.wait calls. 73 bool pred = false; 74 bool pred_function() { 75 return pred == true; 76 } 77 78 class ThrowingMutex 79 { 80 std::atomic_bool locked; 81 unsigned state = 0; 82 ThrowingMutex(const ThrowingMutex&) = delete; 83 ThrowingMutex& operator=(const ThrowingMutex&) = delete; 84 public: 85 ThrowingMutex() { 86 locked = false; 87 } 88 ~ThrowingMutex() = default; 89 90 void lock() { 91 locked = true; 92 if (++state == 2) { 93 assert(pred); // Check that we actually waited until we were signaled. 94 throw 1; // this throw should end up calling terminate() 95 } 96 } 97 98 void unlock() { locked = false; } 99 bool isLocked() const { return locked == true; } 100 }; 101 102 ThrowingMutex mut; 103 std::condition_variable_any cv; 104 105 void signal_me() { 106 while (mut.isLocked()) {} // wait until T1 releases mut inside the cv.wait call. 107 pred = true; 108 cv.notify_one(); 109 } 110 111 typedef std::chrono::system_clock Clock; 112 typedef std::chrono::milliseconds MS; 113 114 int main(int argc, char **argv) { 115 assert(argc == 2); 116 int id = std::stoi(argv[1]); 117 assert(id >= 1 && id <= 6); 118 std::set_terminate(my_terminate); // set terminate after std::stoi because it can throw. 119 MS wait(250); 120 try { 121 mut.lock(); 122 assert(pred == false); 123 support::make_test_thread(signal_me).detach(); 124 switch (id) { 125 case 1: cv.wait(mut); break; 126 case 2: cv.wait(mut, pred_function); break; 127 case 3: cv.wait_for(mut, wait); break; 128 case 4: cv.wait_for(mut, wait, pred_function); break; 129 case 5: cv.wait_until(mut, Clock::now() + wait); break; 130 case 6: cv.wait_until(mut, Clock::now() + wait, pred_function); break; 131 default: assert(false); 132 } 133 } catch (...) {} 134 assert(false); 135 136 return 0; 137 } 138