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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // UNSUPPORTED: libcpp-has-no-threads 10 11 // <thread> 12 13 // class thread 14 15 // template <class F, class ...Args> thread(F&& f, Args&&... args); 16 17 // UNSUPPORTED: sanitizer-new-delete 18 19 #include <thread> 20 #include <new> 21 #include <atomic> 22 #include <cstdlib> 23 #include <cassert> 24 25 #include "test_macros.h" 26 27 std::atomic<unsigned> throw_one(0xFFFF); 28 std::atomic<unsigned> outstanding_new(0); 29 30 31 void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc) 32 { 33 if (throw_one == 0) 34 TEST_THROW(std::bad_alloc()); 35 --throw_one; 36 ++outstanding_new; 37 void* ret = std::malloc(s); 38 if (!ret) std::abort(); // placate MSVC's unchecked malloc warning 39 return ret; 40 } 41 42 void operator delete(void* p) TEST_NOEXCEPT 43 { 44 --outstanding_new; 45 std::free(p); 46 } 47 48 bool f_run = false; 49 50 void f() 51 { 52 f_run = true; 53 } 54 55 class G 56 { 57 int alive_; 58 public: 59 static int n_alive; 60 static bool op_run; 61 62 G() : alive_(1) {++n_alive;} 63 G(const G& g) : alive_(g.alive_) {++n_alive;} 64 ~G() {alive_ = 0; --n_alive;} 65 66 void operator()() 67 { 68 assert(alive_ == 1); 69 assert(n_alive >= 1); 70 op_run = true; 71 } 72 73 void operator()(int i, double j) 74 { 75 assert(alive_ == 1); 76 assert(n_alive >= 1); 77 assert(i == 5); 78 assert(j == 5.5); 79 op_run = true; 80 } 81 }; 82 83 int G::n_alive = 0; 84 bool G::op_run = false; 85 86 #if TEST_STD_VER >= 11 87 88 class MoveOnly 89 { 90 MoveOnly(const MoveOnly&); 91 public: 92 MoveOnly() {} 93 MoveOnly(MoveOnly&&) {} 94 95 void operator()(MoveOnly&&) 96 { 97 } 98 }; 99 100 #endif 101 102 // Test throwing std::bad_alloc 103 //----------------------------- 104 // Concerns: 105 // A Each allocation performed during thread construction should be performed 106 // in the parent thread so that std::terminate is not called if 107 // std::bad_alloc is thrown by new. 108 // B std::thread's constructor should properly handle exceptions and not leak 109 // memory. 110 // Plan: 111 // 1 Create a thread and count the number of allocations, 'N', it performs. 112 // 2 For each allocation performed run a test where that allocation throws. 113 // 2.1 check that the exception can be caught in the parent thread. 114 // 2.2 Check that the functor has not been called. 115 // 2.3 Check that no memory allocated by the creation of the thread is leaked. 116 // 3 Finally check that a thread runs successfully if we throw after 'N+1' 117 // allocations. 118 void test_throwing_new_during_thread_creation() { 119 #ifndef TEST_HAS_NO_EXCEPTIONS 120 throw_one = 0xFFF; 121 { 122 std::thread t(f); 123 t.join(); 124 } 125 const int numAllocs = 0xFFF - throw_one; 126 // i <= numAllocs means the last iteration is expected not to throw. 127 for (int i=0; i <= numAllocs; ++i) { 128 throw_one = i; 129 f_run = false; 130 unsigned old_outstanding = outstanding_new; 131 try { 132 std::thread t(f); 133 assert(i == numAllocs); // Only final iteration will not throw. 134 t.join(); 135 assert(f_run); 136 } catch (std::bad_alloc const&) { 137 assert(i < numAllocs); 138 assert(!f_run); // (2.2) 139 } 140 assert(old_outstanding == outstanding_new); // (2.3) 141 } 142 f_run = false; 143 throw_one = 0xFFF; 144 #endif 145 } 146 147 int main(int, char**) 148 { 149 test_throwing_new_during_thread_creation(); 150 { 151 std::thread t(f); 152 t.join(); 153 assert(f_run == true); 154 } 155 156 { 157 assert(G::n_alive == 0); 158 assert(!G::op_run); 159 { 160 G g; 161 std::thread t(g); 162 t.join(); 163 } 164 assert(G::n_alive == 0); 165 assert(G::op_run); 166 } 167 G::op_run = false; 168 #ifndef TEST_HAS_NO_EXCEPTIONS 169 { 170 try 171 { 172 throw_one = 0; 173 assert(G::n_alive == 0); 174 assert(!G::op_run); 175 std::thread t((G())); 176 assert(false); 177 } 178 catch (...) 179 { 180 throw_one = 0xFFFF; 181 assert(G::n_alive == 0); 182 assert(!G::op_run); 183 } 184 } 185 #endif 186 #if TEST_STD_VER >= 11 187 { 188 assert(G::n_alive == 0); 189 assert(!G::op_run); 190 { 191 G g; 192 std::thread t(g, 5, 5.5); 193 t.join(); 194 } 195 assert(G::n_alive == 0); 196 assert(G::op_run); 197 } 198 { 199 std::thread t = std::thread(MoveOnly(), MoveOnly()); 200 t.join(); 201 } 202 #endif 203 204 return 0; 205 } 206