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