diff options
author | Casey Carter <Casey@Carter.net> | 2019-05-02 21:19:41 +0000 |
---|---|---|
committer | Casey Carter <Casey@Carter.net> | 2019-05-02 21:19:41 +0000 |
commit | b574e112177dbca8eae689d2324d8e5bb7138a06 (patch) | |
tree | 5cfc4066df64b8836b66008d26e0ea657dc1a88d | |
parent | fc40cbd9d8c63e65eed3590ba925321afe782e1d (diff) | |
download | bcm5719-llvm-b574e112177dbca8eae689d2324d8e5bb7138a06.tar.gz bcm5719-llvm-b574e112177dbca8eae689d2324d8e5bb7138a06.zip |
[libc++][test] Remove non-portable assumption that thread's constructor allocates with ::new
Drive-by:
* Fix potential race between check and update of `throw_one` in `operator new`
* Fix latent bug in `operator delete`, which shouldn't decrement `outstanding_new` when passed a null pointer
* Specifically catch the expected `bad_alloc` in `main` instead of `...`
Differential Revision: https://reviews.llvm.org/D50860
llvm-svn: 359827
-rw-r--r-- | libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp index 13c69180a4d..36276b01b92 100644 --- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp +++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp @@ -30,9 +30,10 @@ std::atomic<unsigned> outstanding_new(0); void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc) { - if (throw_one == 0) - TEST_THROW(std::bad_alloc()); - --throw_one; + unsigned expected = throw_one; + do { + if (expected == 0) TEST_THROW(std::bad_alloc()); + } while (!throw_one.compare_exchange_weak(expected, expected - 1)); ++outstanding_new; void* ret = std::malloc(s); if (!ret) std::abort(); // placate MSVC's unchecked malloc warning @@ -41,6 +42,7 @@ void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc) void operator delete(void* p) TEST_NOEXCEPT { + if (!p) return; --outstanding_new; std::free(p); } @@ -108,13 +110,17 @@ public: // B std::thread's constructor should properly handle exceptions and not leak // memory. // Plan: -// 1 Create a thread and count the number of allocations, 'N', it performs. +// 1 Create a thread and count the number of allocations, 'numAllocs', it +// performs. // 2 For each allocation performed run a test where that allocation throws. // 2.1 check that the exception can be caught in the parent thread. // 2.2 Check that the functor has not been called. // 2.3 Check that no memory allocated by the creation of the thread is leaked. -// 3 Finally check that a thread runs successfully if we throw after 'N+1' -// allocations. +// 3 Finally check that a thread runs successfully if we throw after +// 'numAllocs + 1' allocations. + +int numAllocs; + void test_throwing_new_during_thread_creation() { #ifndef TEST_HAS_NO_EXCEPTIONS throw_one = 0xFFF; @@ -122,7 +128,7 @@ void test_throwing_new_during_thread_creation() { std::thread t(f); t.join(); } - const int numAllocs = 0xFFF - throw_one; + numAllocs = 0xFFF - throw_one; // i <= numAllocs means the last iteration is expected not to throw. for (int i=0; i <= numAllocs; ++i) { throw_one = i; @@ -166,7 +172,10 @@ int main(int, char**) } G::op_run = false; #ifndef TEST_HAS_NO_EXCEPTIONS - { + // The test below expects `std::thread` to call `new`, which may not be the + // case for all implementations. + LIBCPP_ASSERT(numAllocs > 0); // libc++ should call new. + if (numAllocs > 0) { try { throw_one = 0; @@ -175,7 +184,7 @@ int main(int, char**) std::thread t((G())); assert(false); } - catch (...) + catch (std::bad_alloc const&) { throw_one = 0xFFFF; assert(G::n_alive == 0); @@ -201,5 +210,5 @@ int main(int, char**) } #endif - return 0; + return 0; } |