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