|
| 1 | +<a id="top"></a> |
| 2 | +# Thread safety in Catch2 |
| 3 | + |
| 4 | +**Contents**<br> |
| 5 | +[Using assertion macros from multiple threads](#using-assertion-macros-from-multiple-threads)<br> |
| 6 | +[examples](#examples)<br> |
| 7 | +[`STATIC_REQUIRE` and `STATIC_CHECK`](#static_require-and-static_check)<br> |
| 8 | +[Fatal errors and multiple threads](#fatal-errors-and-multiple-threads)<br> |
| 9 | +[Performance overhead](#performance-overhead)<br> |
| 10 | + |
| 11 | +> Thread safe assertions were introduced in Catch2 X.Y.Z |
| 12 | +
|
| 13 | +Thread safety in Catch2 is currently limited to all the assertion macros. |
| 14 | +Interacting with benchmark macros, message macros (e.g. `INFO` or `CAPTURE`), |
| 15 | +sections macros, generator macros, or test case macros is not thread-safe. |
| 16 | +The message macros are likely to be made thread-safe in the future, but |
| 17 | +the way sections define test runs is incompatible with user being able |
| 18 | +to spawn threads arbitrarily, thus that limitation is here to stay. |
| 19 | + |
| 20 | +**Important: thread safety in Catch2 is [opt-in](configuration.md#experimental-thread-safety)** |
| 21 | + |
| 22 | + |
| 23 | +## Using assertion macros from multiple threads |
| 24 | + |
| 25 | +The full set of Catch2's runtime assertion macros is thread-safe. However, |
| 26 | +it is important to keep in mind that their semantics might not support |
| 27 | +being used from user-spawned threads. |
| 28 | + |
| 29 | +Specifically, the `REQUIRE` family of assertion macros have semantics |
| 30 | +of stopping the test execution on failure. This is done by throwing |
| 31 | +an exception, but since the user-spawned thread will not have the test-level |
| 32 | +try-catch block ready to catch the test failure exception, failing a |
| 33 | +`REQUIRE` assertion inside this thread will terminate the process. |
| 34 | + |
| 35 | +The `CHECK` family of assertions does not have this issue, because it |
| 36 | +does not try to stop the test execution. |
| 37 | + |
| 38 | +Note that `CHECKED_IF` and `CHECKED_ELSE` are also thread safe (internally |
| 39 | +they are assertion macro + an if). |
| 40 | + |
| 41 | +**`SKIP()`, `FAIL()`, `SUCCEED()` are not assertion macros, and are not |
| 42 | +thread-safe.** |
| 43 | + |
| 44 | + |
| 45 | +## examples |
| 46 | + |
| 47 | +### `REQUIRE` from main thread, `CHECK` from spawned threads |
| 48 | + |
| 49 | +```cpp |
| 50 | +TEST_CASE( "Failed REQUIRE in main thread is fine" ) { |
| 51 | + std::vector<std::jthread> threads; |
| 52 | + for ( size_t t = 0; t < 16; ++t) { |
| 53 | + threads.emplace_back( []() { |
| 54 | + for (size_t i = 0; i < 10'000; ++i) { |
| 55 | + CHECK( true ); |
| 56 | + CHECK( false ); |
| 57 | + } |
| 58 | + } ); |
| 59 | + } |
| 60 | + |
| 61 | + REQUIRE( false ); |
| 62 | +} |
| 63 | +``` |
| 64 | +This will work as expected, that is, the process will finish running |
| 65 | +normally, the test case will fail and there will be the correct count of |
| 66 | +passing and failing assertions (160000 and 160001 respectively). However, |
| 67 | +it is important to understand that when the main thread fails its assertion, |
| 68 | +the spawned threads will keep running. |
| 69 | +
|
| 70 | +
|
| 71 | +### `REQUIRE` from spawned threads |
| 72 | +
|
| 73 | +```cpp |
| 74 | +TEST_CASE( "Successful REQUIRE in spawned thread is fine" ) { |
| 75 | + std::vector<std::jthread> threads; |
| 76 | + for ( size_t t = 0; t < 16; ++t) { |
| 77 | + threads.emplace_back( []() { |
| 78 | + for (size_t i = 0; i < 10'000; ++i) { |
| 79 | + REQUIRE( true ); |
| 80 | + } |
| 81 | + } ); |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | +This will also work as expected, because the `REQUIRE` is successful. |
| 86 | + |
| 87 | +```cpp |
| 88 | +TEST_CASE( "Failed REQUIRE in spawned thread is fine" ) { |
| 89 | + std::vector<std::jthread> threads; |
| 90 | + for ( size_t t = 0; t < 16; ++t) { |
| 91 | + threads.emplace_back( []() { |
| 92 | + for (size_t i = 0; i < 10'000; ++i) { |
| 93 | + REQUIRE( false ); |
| 94 | + } |
| 95 | + } ); |
| 96 | + } |
| 97 | +} |
| 98 | +``` |
| 99 | +This will fail catastrophically and terminate the process. |
| 100 | +
|
| 101 | +
|
| 102 | +## `STATIC_REQUIRE` and `STATIC_CHECK` |
| 103 | +
|
| 104 | +None of `STATIC_REQUIRE`, `STATIC_REQUIRE_FALSE`, `STATIC_CHECK`, and |
| 105 | +`STATIC_CHECK_FALSE` are currently thread safe. This might be surprising |
| 106 | +given that they are a compile-time checks, but they also rely on the |
| 107 | +message macros to register the result with reporter at runtime. |
| 108 | +
|
| 109 | +
|
| 110 | +## Fatal errors and multiple threads |
| 111 | +
|
| 112 | +By default, Catch2 tries to catch fatal errors (POSIX signals/Windows |
| 113 | +Structured Exceptions) and report something useful to the user. This |
| 114 | +always happened on a best-effort basis, but in presence of multiple |
| 115 | +threads and locks the chance of it working decreases. If this starts |
| 116 | +being an issue for you, [you can disable it](configuration.md#other-toggles). |
| 117 | +
|
| 118 | +
|
| 119 | +## Performance overhead |
| 120 | +
|
| 121 | +In the worst case, which is optimized build and assertions using the |
| 122 | +fast path for successful assertions, the performance overhead of using |
| 123 | +the thread-safe assertion implementation can reach 40%. In other cases, |
| 124 | +the overhead will be smaller, between 4% and 20%. |
| 125 | +
|
| 126 | +
|
| 127 | +
|
| 128 | +--- |
| 129 | +
|
| 130 | +[Home](Readme.md#top) |
0 commit comments