-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Make assertions thread-safe #2948
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: devel
Are you sure you want to change the base?
Make assertions thread-safe #2948
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## devel #2948 +/- ##
==========================================
- Coverage 90.98% 90.96% -0.02%
==========================================
Files 199 201 +2
Lines 8612 8635 +23
==========================================
+ Hits 7835 7854 +19
- Misses 777 781 +4 🚀 New features to boost your workflow:
|
…y of Catch2 macros TODO: revert this ugliness when catchorg/Catch2#2948 is merged
Hi @shahsb, I am awaiting further feedback from the maintainer who I have been in contact with regarding this PR over discord. I'm a bit confused by your reviews, are you involved with the Catch2 project? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cannot possibly be sufficient. As just one missing case:
Catch2/src/catch2/internal/catch_test_macro_impl.hpp
Lines 58 to 60 in f51dc98
#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ | |
INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ | |
if( Catch::getResultCapture().lastAssertionPassed() ) |
It may be better to have lastAssertionPassed
(or possibly the whole ResultCapture
) be thread-local.
m_resultCapture.notifyAssertionStarted( m_assertionInfo ); | ||
} | ||
|
||
AssertionHandler::~AssertionHandler() { | ||
auto lock = take_global_lock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be moved inside the if
or is m_completed
written to from other threads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The handler is on stack, so multiple threads shouldn't be writing to it.
Thanks @RedBeard0531, good point. Catch2 has a ton of internal state, this is hard to track down :) It does look like it might be sufficient to make |
@@ -480,6 +484,7 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) { | |||
} | |||
|
|||
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { | |||
auto lock = take_global_lock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Locking in ConsoleReporter is suspect, because nothing enforces that the users use it. It should be done further up the callstack, e.g. in run_context.
void AssertionHandler::handleExpr( ITransientExpression const& expr ) { | ||
auto lock = take_global_lock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these should live in the capture, because it already handles things like assertion fast-path if no reporters have to be notified about a passing assertion.
Speaking of, the current fast path for assertions is a simple counter increment without invoking the reporter. We should be able to have similar fast path (with an atomic counter) after this.
58137e5
to
221d5ef
Compare
Description
This PR makes Catch2 assertion and logging macros thread-safe, allowing threads spawned in test cases to perform checks. It does this by introducing a global lock and locking at entry points called from these macros. This was easier and safer than trying to track down every use of static storage in the library and locking only around those, however, at some point that might be desirable.
A
std::recursive_mutex
is used for the global lock. There is some overhead associated with taking out the lock, however, it's extremely minimal in the uncontested (single-thread) case. In a benchmark locally I found that for a debug build on linux the overhead of the lock is around 300ns on my machine, meaning that a user would have to perform a million assertions in order to add a second of run-time overhead to their program.On release the overhead is far smaller:
MSVC debug:
If this impact is deemed too high I have ideas for reducing the overhead in debug mode and optimizing for the uncontested case.
GitHub Issues
#99
#246
#875
#1043
#1169
#1252
#1302
#1714
#1904
#2641
And probably others