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