mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-19 11:25:38 +02:00
Make assertions thread-safe
This commit is contained in:
@@ -182,6 +182,7 @@ set(IMPL_SOURCES
|
||||
${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
|
||||
${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp
|
||||
${SOURCES_DIR}/internal/catch_getenv.cpp
|
||||
${SOURCES_DIR}/internal/catch_global_lock.cpp
|
||||
${SOURCES_DIR}/internal/catch_istream.cpp
|
||||
${SOURCES_DIR}/internal/catch_jsonwriter.cpp
|
||||
${SOURCES_DIR}/internal/catch_lazy_expr.cpp
|
||||
|
@@ -10,9 +10,12 @@
|
||||
#include <catch2/internal/catch_context.hpp>
|
||||
#include <catch2/internal/catch_debugger.hpp>
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_global_lock.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
|
||||
namespace Catch {
|
||||
// The AssertionHandler API and handleExceptionMatchExpr are used by assertion macros. Everything here must be
|
||||
// locked as catch internals are not thread-safe.
|
||||
|
||||
AssertionHandler::AssertionHandler
|
||||
( StringRef macroName,
|
||||
@@ -22,13 +25,23 @@ namespace Catch {
|
||||
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
|
||||
m_resultCapture( getResultCapture() )
|
||||
{
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
|
||||
}
|
||||
|
||||
AssertionHandler::~AssertionHandler() {
|
||||
auto lock = get_global_lock();
|
||||
if ( !m_completed ) {
|
||||
m_resultCapture.handleIncomplete( m_assertionInfo );
|
||||
}
|
||||
}
|
||||
|
||||
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
|
||||
}
|
||||
void AssertionHandler::handleMessage(ResultWas::OfType resultType, std::string&& message) {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleMessage( m_assertionInfo, resultType, CATCH_MOVE(message), m_reaction );
|
||||
}
|
||||
|
||||
@@ -55,21 +68,26 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void AssertionHandler::handleUnexpectedInflightException() {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
|
||||
}
|
||||
|
||||
void AssertionHandler::handleExceptionThrownAsExpected() {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
|
||||
}
|
||||
void AssertionHandler::handleExceptionNotThrownAsExpected() {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
|
||||
}
|
||||
|
||||
void AssertionHandler::handleUnexpectedExceptionNotThrown() {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
|
||||
}
|
||||
|
||||
void AssertionHandler::handleThrowingCallSkipped() {
|
||||
auto lock = get_global_lock();
|
||||
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
|
||||
}
|
||||
|
||||
|
@@ -34,12 +34,7 @@ namespace Catch {
|
||||
SourceLineInfo const& lineInfo,
|
||||
StringRef capturedExpression,
|
||||
ResultDisposition::Flags resultDisposition );
|
||||
~AssertionHandler() {
|
||||
if ( !m_completed ) {
|
||||
m_resultCapture.handleIncomplete( m_assertionInfo );
|
||||
}
|
||||
}
|
||||
|
||||
~AssertionHandler();
|
||||
|
||||
template<typename T>
|
||||
constexpr void handleExpr( ExprLhs<T> const& expr ) {
|
||||
|
14
src/catch2/internal/catch_global_lock.cpp
Normal file
14
src/catch2/internal/catch_global_lock.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
// 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
|
||||
#include <catch2/internal/catch_global_lock.hpp>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
std::recursive_mutex global_lock;
|
||||
|
||||
} // namespace Catch
|
23
src/catch2/internal/catch_global_lock.hpp
Normal file
23
src/catch2/internal/catch_global_lock.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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
|
||||
|
||||
#ifndef CATCH_GLOBAL_LOCK_HPP_INCLUDED
|
||||
#define CATCH_GLOBAL_LOCK_HPP_INCLUDED
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
extern std::recursive_mutex global_lock;
|
||||
|
||||
inline auto get_global_lock() {
|
||||
return std::unique_lock<std::recursive_mutex>(global_lock);
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
#endif // CATCH_GLOBAL_LOCK_HPP_INCLUDED
|
@@ -8,6 +8,7 @@
|
||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||
#include <catch2/internal/catch_singletons.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
#include <catch2/internal/catch_global_lock.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
@@ -39,12 +40,18 @@ namespace Catch {
|
||||
}
|
||||
};
|
||||
|
||||
ReusableStringStream::ReusableStringStream()
|
||||
: m_index( Singleton<StringStreams>::getMutable().add() ),
|
||||
m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
|
||||
{}
|
||||
// Catch message macros create MessageStreams which hold ReusableStringStream. Since catch internals are not
|
||||
// thread-safe locking is needed and it's easiest to lock at the ReusableStringStream construct/destruct level
|
||||
// instead of poking around StringStreams and Singleton.
|
||||
|
||||
ReusableStringStream::ReusableStringStream() {
|
||||
auto lock = get_global_lock();
|
||||
m_index = Singleton<StringStreams>::getMutable().add();
|
||||
m_oss = Singleton<StringStreams>::getMutable().m_streams[m_index].get();
|
||||
}
|
||||
|
||||
ReusableStringStream::~ReusableStringStream() {
|
||||
auto lock = get_global_lock();
|
||||
static_cast<std::ostringstream*>( m_oss )->str("");
|
||||
m_oss->clear();
|
||||
Singleton<StringStreams>::getMutable().release( m_index );
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <catch2/internal/catch_assertion_handler.hpp>
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_result_type.hpp>
|
||||
#include <catch2/internal/catch_global_lock.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
@@ -418,19 +419,25 @@ namespace Catch {
|
||||
m_unfinishedSections.push_back(CATCH_MOVE(endInfo));
|
||||
}
|
||||
|
||||
// Catch benchmark macros call these functions. Since catch internals are not thread-safe locking is needed.
|
||||
|
||||
void RunContext::benchmarkPreparing( StringRef name ) {
|
||||
auto lock = get_global_lock();
|
||||
auto _ = scopedDeactivate( *m_outputRedirect );
|
||||
m_reporter->benchmarkPreparing( name );
|
||||
}
|
||||
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
|
||||
auto lock = get_global_lock();
|
||||
auto _ = scopedDeactivate( *m_outputRedirect );
|
||||
m_reporter->benchmarkStarting( info );
|
||||
}
|
||||
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
|
||||
auto lock = get_global_lock();
|
||||
auto _ = scopedDeactivate( *m_outputRedirect );
|
||||
m_reporter->benchmarkEnded( stats );
|
||||
}
|
||||
void RunContext::benchmarkFailed( StringRef error ) {
|
||||
auto lock = get_global_lock();
|
||||
auto _ = scopedDeactivate( *m_outputRedirect );
|
||||
m_reporter->benchmarkFailed( error );
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <catch2/internal/catch_console_width.hpp>
|
||||
#include <catch2/reporters/catch_reporter_helpers.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_global_lock.hpp>
|
||||
#include <catch2/catch_get_random_seed.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
@@ -462,7 +463,10 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
|
||||
StreamingReporterBase::sectionEnded(_sectionStats);
|
||||
}
|
||||
|
||||
// Catch benchmark macros call these functions. Since catch internals are not thread-safe locking is needed.
|
||||
|
||||
void ConsoleReporter::benchmarkPreparing( StringRef name ) {
|
||||
auto lock = get_global_lock();
|
||||
lazyPrintWithoutClosingBenchmarkTable();
|
||||
|
||||
auto nameCol = TextFlow::Column( static_cast<std::string>( name ) )
|
||||
@@ -480,6 +484,7 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) {
|
||||
}
|
||||
|
||||
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
|
||||
auto lock = get_global_lock();
|
||||
(*m_tablePrinter) << info.samples << ColumnBreak()
|
||||
<< info.iterations << ColumnBreak();
|
||||
if ( !m_config->benchmarkNoAnalysis() ) {
|
||||
@@ -489,6 +494,7 @@ void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
|
||||
( *m_tablePrinter ) << OutputFlush{};
|
||||
}
|
||||
void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
|
||||
auto lock = get_global_lock();
|
||||
if (m_config->benchmarkNoAnalysis())
|
||||
{
|
||||
(*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
|
||||
@@ -506,6 +512,7 @@ void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
|
||||
}
|
||||
|
||||
void ConsoleReporter::benchmarkFailed( StringRef error ) {
|
||||
auto lock = get_global_lock();
|
||||
auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream );
|
||||
(*m_tablePrinter)
|
||||
<< "Benchmark failed (" << error << ')'
|
||||
|
Reference in New Issue
Block a user