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

source code of libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp