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
27 std::atomic<unsigned> throw_one(0xFFFF);
28 std::atomic<unsigned> outstanding_new(0);
29
30
operator new(std::size_t s)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) {
40 std::abort(); // placate MSVC's unchecked malloc warning (assert() won't silence it)
41 }
42 return ret;
43 }
44
operator delete(void * p)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 struct F {
55 std::vector<int> v_; // so f's copy-ctor calls operator new
FF56 explicit F() : v_(10) {}
operator ()F57 void operator()() const { f_run = true; }
58 };
59 F f;
60
61 class G
62 {
63 int alive_;
64 public:
65 static int n_alive;
66 static bool op_run;
67
G()68 G() : alive_(1) {++n_alive;}
G(const G & g)69 G(const G& g) : alive_(g.alive_) {++n_alive;}
~G()70 ~G() {alive_ = 0; --n_alive;}
71
operator ()()72 void operator()()
73 {
74 assert(alive_ == 1);
75 assert(n_alive >= 1);
76 op_run = true;
77 }
78
operator ()(int i,double j)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
89 int G::n_alive = 0;
90 bool G::op_run = false;
91
92 #if TEST_STD_VER >= 11
93
94 class MoveOnly
95 {
96 MoveOnly(const MoveOnly&);
97 public:
MoveOnly()98 MoveOnly() {}
MoveOnly(MoveOnly &&)99 MoveOnly(MoveOnly&&) {}
100
operator ()(MoveOnly &&)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
126 int numAllocs;
127
test_throwing_new_during_thread_creation()128 void 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
main(int,char **)157 int 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