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