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) std::abort(); // placate MSVC's unchecked malloc warning
40 return ret;
41 }
42
operator delete(void * p)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 struct F {
53 std::vector<int> v_; // so f's copy-ctor calls operator new
FF54 explicit F() : v_(10) {}
operator ()F55 void operator()() const { f_run = true; }
56 };
57 F f;
58
59 class G
60 {
61 int alive_;
62 public:
63 static int n_alive;
64 static bool op_run;
65
G()66 G() : alive_(1) {++n_alive;}
G(const G & g)67 G(const G& g) : alive_(g.alive_) {++n_alive;}
~G()68 ~G() {alive_ = 0; --n_alive;}
69
operator ()()70 void operator()()
71 {
72 assert(alive_ == 1);
73 assert(n_alive >= 1);
74 op_run = true;
75 }
76
operator ()(int i,double j)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:
MoveOnly()96 MoveOnly() {}
MoveOnly(MoveOnly &&)97 MoveOnly(MoveOnly&&) {}
98
operator ()(MoveOnly &&)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
test_throwing_new_during_thread_creation()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_WITH_LIBRARY_INTERNAL_ALLOCATIONS(old_outstanding == outstanding_new); // (2.3)
149 }
150 f_run = false;
151 throw_one = 0xFFF;
152 #endif
153 }
154
main(int,char **)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