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