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