onqtam | c9b4e1f | 2018-08-17 14:20:59 +0300 | [diff] [blame] | 1 | #include "doctest.h" |
| 2 | |
| 3 | DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN |
| 4 | #include <thread> |
onqtam | a2e77c7 | 2018-08-20 11:05:05 +0300 | [diff] [blame^] | 5 | #include <mutex> |
| 6 | #include <exception> |
| 7 | #include <stdexcept> |
onqtam | c9b4e1f | 2018-08-17 14:20:59 +0300 | [diff] [blame] | 8 | DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END |
| 9 | |
onqtam | a2e77c7 | 2018-08-20 11:05:05 +0300 | [diff] [blame^] | 10 | std::exception_ptr g_exception_ptr = nullptr; |
| 11 | std::mutex g_mutex; |
| 12 | |
onqtam | c9b4e1f | 2018-08-17 14:20:59 +0300 | [diff] [blame] | 13 | TEST_CASE("threads...") { |
onqtam | a2e77c7 | 2018-08-20 11:05:05 +0300 | [diff] [blame^] | 14 | auto call_from_thread = [](int value) { |
| 15 | INFO("print me!"); |
| 16 | // one of these has to fail |
| 17 | CHECK(value == 1); |
| 18 | CHECK(value == 2); |
| 19 | }; |
| 20 | |
| 21 | int data_1 = 1; |
| 22 | int data_2 = 2; |
| 23 | CAPTURE(data_1); // will not be used for assertions in other threads |
| 24 | |
| 25 | // subcases have to be used only in the main thread (where the test runner is) |
| 26 | SUBCASE("test runner thread") { |
| 27 | call_from_thread(data_1); |
| 28 | } |
| 29 | |
| 30 | // normal threads which are assumed not to throw |
| 31 | SUBCASE("spawned threads") { |
| 32 | std::thread t1(call_from_thread, data_1); |
| 33 | std::thread t2(call_from_thread, data_2); |
| 34 | |
| 35 | t1.join(); |
| 36 | t2.join(); |
| 37 | } |
| 38 | |
| 39 | // exceptions from threads (that includes failing REQUIRE asserts) have to be handled explicitly |
| 40 | SUBCASE("spawned threads with exception propagation") { |
| 41 | auto might_throw = []() { |
| 42 | try { |
| 43 | REQUIRE(1 == 1); |
| 44 | REQUIRE(1 == 2); // will fail and throw an exception |
| 45 | MESSAGE("not reached!"); |
| 46 | } catch(...) { |
| 47 | // make sure there are no races when dealing with the global exception ptr |
| 48 | std::lock_guard<std::mutex> lock(g_mutex); |
| 49 | |
| 50 | // set the global exception pointer in case of an exception - might overwrite |
| 51 | // another exception but here we care about propagating any exception - not all |
| 52 | g_exception_ptr = std::current_exception(); |
| 53 | } |
| 54 | }; |
| 55 | std::thread t1(might_throw); |
| 56 | std::thread t2(might_throw); |
| 57 | |
| 58 | t1.join(); |
| 59 | t2.join(); |
| 60 | |
| 61 | // if any thread has thrown an exception - rethrow it |
| 62 | if(g_exception_ptr) |
| 63 | std::rethrow_exception(g_exception_ptr); |
| 64 | } |
onqtam | c9b4e1f | 2018-08-17 14:20:59 +0300 | [diff] [blame] | 65 | } |