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