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