219 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// <thread>10 11// class thread12 13// template <class F, class ...Args> thread(F&& f, Args&&... args);14 15// UNSUPPORTED: no-threads16// UNSUPPORTED: sanitizer-new-delete17 18#include <thread>19#include <new>20#include <atomic>21#include <cstdlib>22#include <cassert>23#include <vector>24 25#include "test_macros.h"26 27std::atomic<unsigned> throw_one(0xFFFF);28std::atomic<unsigned> outstanding_new(0);29 30 31void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)32{33 unsigned expected = throw_one;34 do {35 if (expected == 0) TEST_THROW(std::bad_alloc());36 } while (!throw_one.compare_exchange_weak(expected, expected - 1));37 ++outstanding_new;38 void* ret = std::malloc(s);39 if (!ret) {40 std::abort(); // placate MSVC's unchecked malloc warning (assert() won't silence it)41 }42 return ret;43}44 45void operator delete(void* p) TEST_NOEXCEPT46{47 if (!p) return;48 --outstanding_new;49 std::free(p);50}51 52bool f_run = false;53 54struct F {55 std::vector<int> v_; // so f's copy-ctor calls operator new56 explicit F() : v_(10) {}57 void operator()() const { f_run = true; }58};59F f;60 61class G62{63 int alive_;64public:65 static int n_alive;66 static bool op_run;67 68 G() : alive_(1) {++n_alive;}69 G(const G& g) : alive_(g.alive_) {++n_alive;}70 ~G() {alive_ = 0; --n_alive;}71 72 void operator()()73 {74 assert(alive_ == 1);75 assert(n_alive >= 1);76 op_run = true;77 }78 79 void operator()(int i, double j)80 {81 assert(alive_ == 1);82 assert(n_alive >= 1);83 assert(i == 5);84 assert(j == 5.5);85 op_run = true;86 }87};88 89int G::n_alive = 0;90bool G::op_run = false;91 92#if TEST_STD_VER >= 1193 94class MoveOnly95{96 MoveOnly(const MoveOnly&);97public:98 MoveOnly() {}99 MoveOnly(MoveOnly&&) {}100 101 void operator()(MoveOnly&&)102 {103 }104};105 106#endif107 108// Test throwing std::bad_alloc109//-----------------------------110// Concerns:111// A Each allocation performed during thread construction should be performed112// in the parent thread so that std::terminate is not called if113// std::bad_alloc is thrown by new.114// B std::thread's constructor should properly handle exceptions and not leak115// memory.116// Plan:117// 1 Create a thread and count the number of allocations, 'numAllocs', it118// performs.119// 2 For each allocation performed run a test where that allocation throws.120// 2.1 check that the exception can be caught in the parent thread.121// 2.2 Check that the functor has not been called.122// 2.3 Check that no memory allocated by the creation of the thread is leaked.123// 3 Finally check that a thread runs successfully if we throw after124// 'numAllocs + 1' allocations.125 126int numAllocs;127 128void test_throwing_new_during_thread_creation() {129#ifndef TEST_HAS_NO_EXCEPTIONS130 throw_one = 0xFFF;131 {132 std::thread t(f);133 t.join();134 }135 numAllocs = 0xFFF - throw_one;136 // i <= numAllocs means the last iteration is expected not to throw.137 for (int i=0; i <= numAllocs; ++i) {138 throw_one = i;139 f_run = false;140 unsigned old_outstanding = outstanding_new;141 try {142 std::thread t(f);143 assert(i == numAllocs); // Only final iteration will not throw.144 t.join();145 assert(f_run);146 } catch (std::bad_alloc const&) {147 assert(i < numAllocs);148 assert(!f_run); // (2.2)149 }150 ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(old_outstanding == outstanding_new); // (2.3)151 }152 f_run = false;153 throw_one = 0xFFF;154#endif155}156 157int main(int, char**)158{159 test_throwing_new_during_thread_creation();160 {161 std::thread t(f);162 t.join();163 assert(f_run == true);164 }165 166 {167 assert(G::n_alive == 0);168 assert(!G::op_run);169 {170 G g;171 std::thread t(g);172 t.join();173 }174 assert(G::n_alive == 0);175 assert(G::op_run);176 }177 G::op_run = false;178#ifndef TEST_HAS_NO_EXCEPTIONS179 // The test below expects `std::thread` to call `new`, which may not be the180 // case for all implementations.181 LIBCPP_ASSERT(numAllocs > 0); // libc++ should call new.182 if (numAllocs > 0) {183 try184 {185 throw_one = 0;186 assert(G::n_alive == 0);187 assert(!G::op_run);188 std::thread t((G()));189 assert(false);190 }191 catch (std::bad_alloc const&)192 {193 throw_one = 0xFFFF;194 assert(G::n_alive == 0);195 assert(!G::op_run);196 }197 }198#endif199#if TEST_STD_VER >= 11200 {201 assert(G::n_alive == 0);202 assert(!G::op_run);203 {204 G g;205 std::thread t(g, 5, 5.5);206 t.join();207 }208 assert(G::n_alive == 0);209 assert(G::op_run);210 }211 {212 std::thread t = std::thread(MoveOnly(), MoveOnly());213 t.join();214 }215#endif216 217 return 0;218}219