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