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.condvar/notify_one.pass.cpp =================================================================== --- test/std/thread/thread.condition/thread.condition.condvar/notify_one.pass.cpp +++ test/std/thread/thread.condition/thread.condition.condvar/notify_one.pass.cpp @@ -20,12 +20,13 @@ #include #include +#include "test_atomic.h" + std::condition_variable cv; std::mutex mut; -int test0 = 0; -int test1 = 0; -int test2 = 0; +AtomicInt test1(0); +AtomicInt test2(0); void f1() { @@ -64,11 +65,13 @@ } if (test1 == 2) { + assert(test2 == 1); t1.join(); test1 = 0; } else if (test2 == 2) { + assert(test1 == 1); t2.join(); test2 = 0; } @@ -81,11 +84,13 @@ } if (test1 == 2) { + assert(test2 == 0); t1.join(); test1 = 0; } else if (test2 == 2) { + assert(test1 == 0); t2.join(); test2 = 0; } 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/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp =================================================================== --- test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp +++ test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp @@ -22,6 +22,8 @@ #include #include +#include "test_macros.h" + unsigned throw_one = 0xFFFF; void* operator new(std::size_t s) throw(std::bad_alloc) @@ -75,7 +77,7 @@ int G::n_alive = 0; bool G::op_run = false; -#ifndef _LIBCPP_HAS_NO_VARIADICS +#if TEST_STD_VER >= 11 class MoveOnly { @@ -137,7 +139,7 @@ assert(!G::op_run); } } -#ifndef _LIBCPP_HAS_NO_VARIADICS +#if TEST_STD_VER >= 11 { assert(G::n_alive == 0); assert(!G::op_run); @@ -150,5 +152,5 @@ std::thread t = std::thread(MoveOnly(), MoveOnly()); t.join(); } -#endif // _LIBCPP_HAS_NO_VARIADICS +#endif } Index: test/std/thread/thread.threads/thread.thread.class/thread.thread.destr/dtor.pass.cpp =================================================================== --- test/std/thread/thread.threads/thread.thread.class/thread.thread.destr/dtor.pass.cpp +++ test/std/thread/thread.threads/thread.thread.class/thread.thread.destr/dtor.pass.cpp @@ -9,8 +9,6 @@ // // UNSUPPORTED: libcpp-has-no-threads -// NOTE: TSAN will report this test as leaking a thread. -// XFAIL: tsan // @@ -47,7 +45,7 @@ void f1() { - std::exit(0); + std::_Exit(0); } int main() 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