Skip to content

Commit b574e11

Browse files
committed
[libc++][test] Remove non-portable assumption that thread's constructor allocates with ::new
Drive-by: * Fix potential race between check and update of `throw_one` in `operator new` * Fix latent bug in `operator delete`, which shouldn't decrement `outstanding_new` when passed a null pointer * Specifically catch the expected `bad_alloc` in `main` instead of `...` Differential Revision: https://reviews.llvm.org/D50860 llvm-svn: 359827
1 parent fc40cbd commit b574e11

File tree

1 file changed

+19
-10
lines changed
  • libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr

1 file changed

+19
-10
lines changed

libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ std::atomic<unsigned> outstanding_new(0);
3030

3131
void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
3232
{
33-
if (throw_one == 0)
34-
TEST_THROW(std::bad_alloc());
35-
--throw_one;
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));
3637
++outstanding_new;
3738
void* ret = std::malloc(s);
3839
if (!ret) std::abort(); // placate MSVC's unchecked malloc warning
@@ -41,6 +42,7 @@ void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
4142

4243
void operator delete(void* p) TEST_NOEXCEPT
4344
{
45+
if (!p) return;
4446
--outstanding_new;
4547
std::free(p);
4648
}
@@ -108,21 +110,25 @@ class MoveOnly
108110
// B std::thread's constructor should properly handle exceptions and not leak
109111
// memory.
110112
// Plan:
111-
// 1 Create a thread and count the number of allocations, 'N', it performs.
113+
// 1 Create a thread and count the number of allocations, 'numAllocs', it
114+
// performs.
112115
// 2 For each allocation performed run a test where that allocation throws.
113116
// 2.1 check that the exception can be caught in the parent thread.
114117
// 2.2 Check that the functor has not been called.
115118
// 2.3 Check that no memory allocated by the creation of the thread is leaked.
116-
// 3 Finally check that a thread runs successfully if we throw after 'N+1'
117-
// allocations.
119+
// 3 Finally check that a thread runs successfully if we throw after
120+
// 'numAllocs + 1' allocations.
121+
122+
int numAllocs;
123+
118124
void test_throwing_new_during_thread_creation() {
119125
#ifndef TEST_HAS_NO_EXCEPTIONS
120126
throw_one = 0xFFF;
121127
{
122128
std::thread t(f);
123129
t.join();
124130
}
125-
const int numAllocs = 0xFFF - throw_one;
131+
numAllocs = 0xFFF - throw_one;
126132
// i <= numAllocs means the last iteration is expected not to throw.
127133
for (int i=0; i <= numAllocs; ++i) {
128134
throw_one = i;
@@ -166,7 +172,10 @@ int main(int, char**)
166172
}
167173
G::op_run = false;
168174
#ifndef TEST_HAS_NO_EXCEPTIONS
169-
{
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) {
170179
try
171180
{
172181
throw_one = 0;
@@ -175,7 +184,7 @@ int main(int, char**)
175184
std::thread t((G()));
176185
assert(false);
177186
}
178-
catch (...)
187+
catch (std::bad_alloc const&)
179188
{
180189
throw_one = 0xFFFF;
181190
assert(G::n_alive == 0);
@@ -201,5 +210,5 @@ int main(int, char**)
201210
}
202211
#endif
203212

204-
return 0;
213+
return 0;
205214
}

0 commit comments

Comments
 (0)