Index: test/std/thread/thread.condition/notify_all_at_thread_exit.pass.cpp =================================================================== --- test/std/thread/thread.condition/notify_all_at_thread_exit.pass.cpp +++ test/std/thread/thread.condition/notify_all_at_thread_exit.pass.cpp @@ -36,9 +36,10 @@ int main() { std::unique_lock lk(mut); - std::thread(func).detach(); + std::thread t(func); Clock::time_point t0 = Clock::now(); cv.wait(lk); Clock::time_point t1 = Clock::now(); assert(t1-t0 > ms(250)); + t.join(); } Index: test/std/thread/thread.condition/thread.condition.condvarany/wait.exception.pass.cpp =================================================================== --- test/std/thread/thread.condition/thread.condition.condvarany/wait.exception.pass.cpp +++ /dev/null @@ -1,63 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// UNSUPPORTED: libcpp-has-no-threads - -#include -#include -#include -#include -#include -#include - -void f1() -{ - std::exit(0); -} - -struct Mutex -{ - unsigned state = 0; - Mutex() = default; - ~Mutex() = default; - Mutex(const Mutex&) = delete; - Mutex& operator=(const Mutex&) = delete; - - void lock() - { - if (++state == 2) - throw 1; // this throw should end up calling terminate() - } - - void unlock() {} -}; - -Mutex mut; -std::condition_variable_any cv; - -void -signal_me() -{ - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - cv.notify_one(); -} - -int -main() -{ - std::set_terminate(f1); - try - { - std::thread(signal_me).detach(); - mut.lock(); - cv.wait(mut); - } - catch (...) {} - assert(false); -} Index: test/std/thread/thread.condition/thread.condition.condvarany/wait_for.exception.pass.cpp =================================================================== --- test/std/thread/thread.condition/thread.condition.condvarany/wait_for.exception.pass.cpp +++ /dev/null @@ -1,63 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// UNSUPPORTED: libcpp-has-no-threads - -#include -#include -#include -#include -#include -#include - -void f1() -{ - std::exit(0); -} - -struct Mutex -{ - unsigned state = 0; - Mutex() = default; - ~Mutex() = default; - Mutex(const Mutex&) = delete; - Mutex& operator=(const Mutex&) = delete; - - void lock() - { - if (++state == 2) - throw 1; // this throw should end up calling terminate() - } - - void unlock() {} -}; - -Mutex mut; -std::condition_variable_any cv; - -void -signal_me() -{ - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - cv.notify_one(); -} - -int -main() -{ - std::set_terminate(f1); - try - { - std::thread(signal_me).detach(); - mut.lock(); - cv.wait_for(mut, std::chrono::milliseconds(250)); - } - catch (...) {} - assert(false); -} Index: test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcpp-has-no-threads + +// + +// class condition_variable_any; + +// RUN: %build +// RUN: %run 1 +// RUN: %run 2 +// RUN: %run 3 +// RUN: %run 4 +// RUN: %run 5 +// RUN: %run 6 + +// ----------------------------------------------------------------------------- +// Overview +// Check that std::terminate is called if wait(...) fails to meet it's post +// conditions. This can happens when reacquiring the mutex throws +// an exception. +// +// The following methods are tested within this file +// 1. void wait(Lock& lock); +// 2. void wait(Lock& lock, Pred); +// 3. void wait_for(Lock& lock, Duration); +// 4. void wait_for(Lock& lock, Duration, Pred); +// 5. void wait_until(Lock& lock, TimePoint); +// 6. void wait_until(Lock& lock, TimePoint, Pred); +// +// Plan +// 1 Create a mutex type, 'ThrowingMutex', that throws when the lock is aquired +// for the *second* time. +// +// 2 Replace the terminate handler with one that exits with a '0' exit code. +// +// 3 Create a 'condition_variable_any' object 'cv' and a 'ThrowingMutex' +// object 'm' and lock 'm'. +// +// 4 Start a thread 'T2' that will notify 'cv' once 'm' has been unlocked. +// +// 5 From the main thread call the specified wait method on 'cv' with 'm'. +// When 'T2' notifies 'cv' and the wait method attempts to re-lock +// 'm' an exception will be thrown from 'm.lock()'. +// +// 6 Check that control flow does not return from the wait method and that +// terminate is called (If the program exits with a 0 exit code we know +// that terminate has been called) + + +#include +#include +#include +#include +#include +#include + +#include "test_atomic.h" + +void my_terminate() { + std::_Exit(0); // Use _Exit to prevent cleanup from taking place. +} + +// The predicate used in the cv.wait calls. +bool pred = false; +bool pred_function() { + return pred == true; +} + +class ThrowingMutex +{ + AtomicBool locked; + unsigned state = 0; + ThrowingMutex(const ThrowingMutex&) = delete; + ThrowingMutex& operator=(const ThrowingMutex&) = delete; +public: + ThrowingMutex() = default; + ~ThrowingMutex() = default; + + void lock() { + locked = true; + if (++state == 2) { + assert(pred); // Check that we actually waited until we were signaled. + throw 1; // this throw should end up calling terminate() + } + } + + void unlock() { locked = false; } + bool isLocked() const { return locked == true; } +}; + +ThrowingMutex mut; +std::condition_variable_any cv; + +void signal_me() { + while (mut.isLocked()) {} // wait until T1 releases mut inside the cv.wait call. + pred = true; + cv.notify_one(); +} + +typedef std::chrono::system_clock Clock; +typedef std::chrono::milliseconds MS; + +int main(int argc, char** argv) { + assert(argc == 2); + int id = std::stoi(argv[1]); + assert(id >= 1 && id <= 6); + std::set_terminate(my_terminate); // set terminate after std::stoi because it can throw. + MS wait(250); + try { + mut.lock(); + assert(pred == false); + std::thread(signal_me).detach(); + switch (id) { + case 1: cv.wait(mut); break; + case 2: cv.wait(mut, pred_function); break; + case 3: cv.wait_for(mut, wait); break; + case 4: cv.wait_for(mut, wait, pred_function); break; + case 5: cv.wait_until(mut, Clock::now() + wait); break; + case 6: cv.wait_until(mut, Clock::now() + wait, pred_function); break; + default: assert(false); + } + } catch (...) {} + assert(false); +} Index: test/support/test_atomic.h =================================================================== --- /dev/null +++ test/support/test_atomic.h @@ -0,0 +1,109 @@ +#ifndef SUPPORT_TEST_ATOMIC_H +#define SUPPORT_TEST_ATOMIC_H + +// If the atomic memory order macros are defined then assume +// the compiler supports the required atomic builtins. +#if !defined(__ATOMIC_SEQ_CST) +#define TEST_HAS_NO_ATOMICS +#endif + +template +class Atomic { + ValType value; + Atomic(Atomic const&); + Atomic& operator=(Atomic const&); + Atomic& operator=(Atomic const&) volatile; +private: + enum { +#if !defined(TEST_HAS_NO_ATOMICS) + AO_Relaxed = __ATOMIC_RELAXED, + AO_Seq = __ATOMIC_SEQ_CST +#else + AO_Relaxed, + AO_Seq +#endif + }; + template + static inline void atomic_store_imp(Tp* dest, FromType from, int order = AO_Seq) { +#if !defined(TEST_HAS_NO_ATOMICS) + __atomic_store_n(dest, from, order); +#else + *dest = from; +#endif + } + + template + static inline Tp atomic_load_imp(Tp* from, int order = AO_Seq) { +#if !defined(TEST_HAS_NO_ATOMICS) + return __atomic_load_n(from, order); +#else + return *from; +#endif + } + + template + static inline Tp atomic_add_imp(Tp* val, AddType add, int order = AO_Seq) { +#if !defined(TEST_HAS_NO_ATOMICS) + return __atomic_add_fetch(val, add, order); +#else + return *val += add; +#endif + } + + template + static inline Tp atomic_exchange_imp(Tp* val, Tp other, int order = AO_Seq) { +#if !defined(TEST_HAS_NO_ATOMICS) + return __atomic_exchange_n(val, other, order); +#else + Tp old = *val; + *val = other; + return old; +#endif + } +public: + Atomic() : value(0) {} + Atomic(ValType x) : value(x) {} + + ValType operator=(ValType val) { + atomic_store_imp(&value, val); + return val; + } + + ValType operator=(ValType val) volatile { + atomic_store_imp(&value, val); + return val; + } + + ValType load() const volatile { return atomic_load_imp(&value); } + void store(ValType val) volatile { atomic_store_imp(&value, val); } + + ValType relaxedLoad() const volatile { return atomic_load_imp(&value, AO_Relaxed); } + void relaxedStore(ValType val) volatile { atomic_store_imp(&value, val, AO_Relaxed); } + + ValType exchange(ValType other) volatile { return atomic_exchange_imp(&value, other); } + bool testAndSet() volatile { return atomic_exchange_imp(&value, 1); } + void clear() volatile { atomic_store_imp(&value, 0); } + + operator ValType() const { return atomic_load_imp(&value); } + operator ValType() const volatile { return atomic_load_imp(&value); } + + ValType operator+=(ValType val) { return atomic_add_imp(&value, val); } + ValType operator-=(ValType val) { return atomic_add_imp(&value, -val); } + ValType operator+=(ValType val) volatile { return atomic_add_imp(&value, val); } + ValType operator-=(ValType val) volatile { return atomic_add_imp(&value, -val); } + + ValType operator++() { return *this += 1; } + ValType operator++(int) { return (*this += 1) - 1; } + ValType operator++() volatile { return *this += 1; } + ValType operator++(int) volatile { return (*this += 1) - 1; } + + ValType operator--() { return *this -= 1; } + ValType operator--(int) { return (*this -= 1) + 1; } + ValType operator--() volatile { return *this -= 1; } + ValType operator--(int) volatile { return (*this -= 1) + 1; } +}; + +typedef Atomic AtomicInt; +typedef Atomic AtomicBool; + +#endif // SUPPORT_TEST_ATOMIC_H