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