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