Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/docs
diff options
context:
space:
mode:
authorMartin Hořeňovský2025-07-17 22:58:41 +0200
committerMartin Hořeňovský2025-07-24 10:10:00 +0200
commit2a8a8a7210513fc2f6ae5e65986c0f2d730c1537 (patch)
tree73483631c78c8c6261b3e5aa1fb21b55dac82ee7 /docs
parent900a6d55167d415736c8233354e40f8f07a35d36 (diff)
downloadcatch3-2a8a8a7210513fc2f6ae5e65986c0f2d730c1537.tar.gz
Add configuration option to make assertions thread-safe
All the previous refactoring to make the assertion fast paths smaller and faster also allows us to implement the fast paths just with thread-local and atomic variables, without full mutexes. However, the performance overhead of thread-safe assertions is still significant for single threaded usage: | slowdown | Debug | Release | |-----------|--------:|--------:| | fast path | 1.04x | 1.43x | | slow path | 1.16x | 1.22x | Thus, we don't make the assertions thread-safe by default, and instead provide a build-time configuration option that the users can set to get thread-safe assertions. This commit is functional, but it still needs some follow-up work: * We do not need full seq_cst increments for the atomic counters, and using weaker ones can be faster. * We brute-force updating the reporter-friendly totals from internal atomic counters by doing it everywhere. We should properly trace where this is needed instead. * Message macros (`INFO`, `UNSCOPED_INFO`, `CAPTURE`, etc) are not made thread safe in this commit, but they can be made thread safe in the future, by building on top of this work. * Add more tests, including with thread-sanitizer, and compiled examples to the repository. Right now, these changes have been compiled with tsan manually, but these tests are not added to CI. Closes #2948
Diffstat (limited to 'docs')
-rw-r--r--docs/configuration.md17
-rw-r--r--docs/test-fixtures.md2
-rw-r--r--docs/thread-safety.md130
3 files changed, 148 insertions, 1 deletions
diff --git a/docs/configuration.md b/docs/configuration.md
index 61abb7fd..c54776ad 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -14,8 +14,10 @@
[Other toggles](#other-toggles)<br>
[Enabling stringification](#enabling-stringification)<br>
[Disabling exceptions](#disabling-exceptions)<br>
+[Disabling deprecation warnings](#disabling-deprecation-warnings)<br>
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
[Static analysis support](#static-analysis-support)<br>
+[Experimental thread safety](#experimental-thread-safety)<br>
Catch2 is designed to "just work" as much as possible, and most of the
configuration options below are changed automatically during compilation,
@@ -314,6 +316,21 @@ no backwards compatibility guarantees._
are not meant to be runnable, only "scannable".
+## Experimental thread safety
+
+> Introduced in Catch2 X.Y.Z
+
+Catch2 can optionally support thread-safe assertions, that means, multiple
+user-spawned threads can use the assertion macros at the same time. Due
+to the performance cost this imposes even on single-threaded usage, Catch2
+defaults to non-thread-safe assertions.
+
+ CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
+ CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
+
+See [the documentation on thread safety in Catch2](thread-safety.md#top)
+for details on which macros are safe and other notes.
+
---
diff --git a/docs/test-fixtures.md b/docs/test-fixtures.md
index 72a0df95..9496dd16 100644
--- a/docs/test-fixtures.md
+++ b/docs/test-fixtures.md
@@ -4,7 +4,7 @@
**Contents**<br>
[Non-Templated test fixtures](#non-templated-test-fixtures)<br>
[Templated test fixtures](#templated-test-fixtures)<br>
-[Signature-based parameterised test fixtures](#signature-based-parametrised-test-fixtures)<br>
+[Signature-based parameterised test fixtures](#signature-based-parameterised-test-fixtures)<br>
[Template fixtures with types specified in template type lists](#template-fixtures-with-types-specified-in-template-type-lists)<br>
## Non-Templated test fixtures
diff --git a/docs/thread-safety.md b/docs/thread-safety.md
new file mode 100644
index 00000000..218246e6
--- /dev/null
+++ b/docs/thread-safety.md
@@ -0,0 +1,130 @@
+<a id="top"></a>
+# Thread safety in Catch2
+
+**Contents**<br>
+[Using assertion macros from multiple threads](#using-assertion-macros-from-multiple-threads)<br>
+[examples](#examples)<br>
+[`STATIC_REQUIRE` and `STATIC_CHECK`](#static_require-and-static_check)<br>
+[Fatal errors and multiple threads](#fatal-errors-and-multiple-threads)<br>
+[Performance overhead](#performance-overhead)<br>
+
+> Thread safe assertions were introduced in Catch2 X.Y.Z
+
+Thread safety in Catch2 is currently limited to all the assertion macros.
+Interacting with benchmark macros, message macros (e.g. `INFO` or `CAPTURE`),
+sections macros, generator macros, or test case macros is not thread-safe.
+The message macros are likely to be made thread-safe in the future, but
+the way sections define test runs is incompatible with user being able
+to spawn threads arbitrarily, thus that limitation is here to stay.
+
+**Important: thread safety in Catch2 is [opt-in](configuration.md#experimental-thread-safety)**
+
+
+## Using assertion macros from multiple threads
+
+The full set of Catch2's runtime assertion macros is thread-safe. However,
+it is important to keep in mind that their semantics might not support
+being used from user-spawned threads.
+
+Specifically, the `REQUIRE` family of assertion macros have semantics
+of stopping the test execution on failure. This is done by throwing
+an exception, but since the user-spawned thread will not have the test-level
+try-catch block ready to catch the test failure exception, failing a
+`REQUIRE` assertion inside this thread will terminate the process.
+
+The `CHECK` family of assertions does not have this issue, because it
+does not try to stop the test execution.
+
+Note that `CHECKED_IF` and `CHECKED_ELSE` are also thread safe (internally
+they are assertion macro + an if).
+
+**`SKIP()`, `FAIL()`, `SUCCEED()` are not assertion macros, and are not
+thread-safe.**
+
+
+## examples
+
+### `REQUIRE` from main thread, `CHECK` from spawned threads
+
+```cpp
+TEST_CASE( "Failed REQUIRE in main thread is fine" ) {
+ std::vector<std::jthread> threads;
+ for ( size_t t = 0; t < 16; ++t) {
+ threads.emplace_back( []() {
+ for (size_t i = 0; i < 10'000; ++i) {
+ CHECK( true );
+ CHECK( false );
+ }
+ } );
+ }
+
+ REQUIRE( false );
+}
+```
+This will work as expected, that is, the process will finish running
+normally, the test case will fail and there will be the correct count of
+passing and failing assertions (160000 and 160001 respectively). However,
+it is important to understand that when the main thread fails its assertion,
+the spawned threads will keep running.
+
+
+### `REQUIRE` from spawned threads
+
+```cpp
+TEST_CASE( "Successful REQUIRE in spawned thread is fine" ) {
+ std::vector<std::jthread> threads;
+ for ( size_t t = 0; t < 16; ++t) {
+ threads.emplace_back( []() {
+ for (size_t i = 0; i < 10'000; ++i) {
+ REQUIRE( true );
+ }
+ } );
+ }
+}
+```
+This will also work as expected, because the `REQUIRE` is successful.
+
+```cpp
+TEST_CASE( "Failed REQUIRE in spawned thread is fine" ) {
+ std::vector<std::jthread> threads;
+ for ( size_t t = 0; t < 16; ++t) {
+ threads.emplace_back( []() {
+ for (size_t i = 0; i < 10'000; ++i) {
+ REQUIRE( false );
+ }
+ } );
+ }
+}
+```
+This will fail catastrophically and terminate the process.
+
+
+## `STATIC_REQUIRE` and `STATIC_CHECK`
+
+None of `STATIC_REQUIRE`, `STATIC_REQUIRE_FALSE`, `STATIC_CHECK`, and
+`STATIC_CHECK_FALSE` are currently thread safe. This might be surprising
+given that they are a compile-time checks, but they also rely on the
+message macros to register the result with reporter at runtime.
+
+
+## Fatal errors and multiple threads
+
+By default, Catch2 tries to catch fatal errors (POSIX signals/Windows
+Structured Exceptions) and report something useful to the user. This
+always happened on a best-effort basis, but in presence of multiple
+threads and locks the chance of it working decreases. If this starts
+being an issue for you, [you can disable it](configuration.md#other-toggles).
+
+
+## Performance overhead
+
+In the worst case, which is optimized build and assertions using the
+fast path for successful assertions, the performance overhead of using
+the thread-safe assertion implementation can reach 40%. In other cases,
+the overhead will be smaller, between 4% and 20%.
+
+
+
+---
+
+[Home](Readme.md#top)