mirror of
https://github.com/catchorg/Catch2
synced 2026-02-23 16:22:10 +00:00
The original implementation of `UNSCOPED_X` message macros used a
clever hack to make the original implementation simpler: construct
an instance of `ScopedMessage` to manage its lifetime, but store
it in a vector, so its lifetime is not actually scope-based, and
we can manage it through the vector instance.
This hack made it so that the lifetime of the vector that manages
the fake `ScopedMessage`s must be outlived by the vector with the
actual messages. Originally this wasn't a problem, because they both
lived inside the run context instance. However, since then these
vectors became globals and thread-local. When this happened, it
still wasn't a problem; the two globals were declared in the right
order, so they were destroyed in the right order as well.
Then, in f80956a43a, these globals
were turned into magic static globals to improve their behaviour
in MSVC's Debug build mode. This caused their lifetimes to be
runtime-dependent; if a specific test thread added its first scoped
message before it added first unscoped message, the lifetimes
would be correct. If it instead added first unscoped message
before adding first scoped message, then there **might** be
invalid reads during thread destruction.
The fix is simple: do things properly and manage the lifetime of
messages in `UNSCOPED_X` explicitly. Then we don't have to deal
with the destruction of fake `ScopedMessage`s while the thread is
being destroyed, and the lifetime of the two vectors is no longer
tied together.
I also threw them both into a new type, to encapsulate some of the
unscoped message logic.
65 lines
1.7 KiB
C++
65 lines
1.7 KiB
C++
|
|
// Copyright Catch2 Authors
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE.txt or copy at
|
|
// https://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/**\file
|
|
* Test that assertions and messages are thread-safe.
|
|
*
|
|
* This is done by spamming assertions and messages on multiple subthreads.
|
|
* In manual, this reliably causes segfaults if the test is linked against
|
|
* a non-thread-safe version of Catch2.
|
|
*
|
|
* The CTest test definition should also verify that the final assertion
|
|
* count is correct.
|
|
*/
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
TEST_CASE( "Failed REQUIRE in the main thread is fine", "[!shouldfail]" ) {
|
|
std::vector<std::thread> threads;
|
|
for ( size_t t = 0; t < 4; ++t) {
|
|
threads.emplace_back( [t]() {
|
|
CAPTURE(t);
|
|
for (size_t i = 0; i < 100; ++i) {
|
|
CAPTURE(i);
|
|
CHECK( false );
|
|
CHECK( true );
|
|
}
|
|
} );
|
|
}
|
|
|
|
for (auto& t : threads) {
|
|
t.join();
|
|
}
|
|
|
|
REQUIRE( false );
|
|
}
|
|
|
|
TEST_CASE( "Using unscoped messages in sibling threads", "[!shouldfail]" ) {
|
|
std::vector<std::thread> threads;
|
|
for ( size_t t = 0; t < 4; ++t) {
|
|
threads.emplace_back( [t]() {
|
|
UNSCOPED_INFO("thread " << t << " start");
|
|
for (size_t i = 0; i < 100; ++i) {
|
|
for (size_t j = 0; j < 4; ++j) {
|
|
UNSCOPED_INFO("t=" << i << ", " << j);
|
|
}
|
|
CHECK( false );
|
|
}
|
|
} );
|
|
}
|
|
|
|
for (auto& t : threads) {
|
|
t.join();
|
|
}
|
|
|
|
REQUIRE( false );
|
|
}
|