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