Skip to content

Commit

Permalink
Config no multithreading (doctest#625)
Browse files Browse the repository at this point in the history
* Add DOCTEST_CONFIG_NO_ATOMICS

* Fix

* Fix cmake stuff and ambiguity issue

* No mutices when multithreading is disabled

* Add WASI platform

* Fix various warnings

* No `thread_local`s when multithreading is disabled

* Fix tests

* Add docs

* Fix docs

* Various fixes, simplifications and additional tests

* Small docs amendment

* Tiny style fix
  • Loading branch information
Saalvage authored Mar 19, 2022
1 parent 431a11b commit 0fc371e
Show file tree
Hide file tree
Showing 7 changed files with 1,747 additions and 66 deletions.
11 changes: 11 additions & 0 deletions doc/markdown/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Defining something ```globally``` means for every source file of the binary (exe
- [**```DOCTEST_CONFIG_POSIX_SIGNALS```**](#doctest_config_posix_signals)
- [**```DOCTEST_CONFIG_NO_POSIX_SIGNALS```**](#doctest_config_no_posix_signals)
- [**```DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS```**](#doctest_config_include_type_traits)
- [**```DOCTEST_CONFIG_NO_MULTITHREADING```**](#doctest_config_no_multithreading)
- [**```DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS```**](#doctest_config_no_multi_lane_atomics)
- [**```DOCTEST_CONFIG_ASSERTS_RETURN_VALUES```**](#doctest_config_asserts_return_values)
- [**```DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED```**](#doctest_config_evaluate_asserts_even_when_disabled)
Expand Down Expand Up @@ -237,6 +238,16 @@ This can be used to include the ```<type_traits>``` C++11 header. That in turn w
This can be defined both globally and in specific source files only.
### **```DOCTEST_CONFIG_NO_MULTITHREADING```**
This can be used to disable all multithreading support.
Speeds up single threaded applications.
Includes [**```DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS```**](#doctest_config_no_multi_lane_atomics).
This should be defined only in the source file where the library is implemented (it's relevant only there).
### **```DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS```**
This can be used to disable multi lane atomics. Multi lane atomics can speed up highly parallel use of assert statements, but have a small overhead for single threaded applications.
Expand Down
77 changes: 49 additions & 28 deletions doctest/doctest.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,16 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH

#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
!defined(__EMSCRIPTEN__)
!defined(__EMSCRIPTEN__) && !defined(__wasi__)
#define DOCTEST_CONFIG_POSIX_SIGNALS
#endif // _WIN32
#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
#undef DOCTEST_CONFIG_POSIX_SIGNALS
#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS

#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \
|| defined(__wasi__)
#define DOCTEST_CONFIG_NO_EXCEPTIONS
#endif // no exceptions
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
Expand All @@ -290,6 +291,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS

#ifdef __wasi__
#define DOCTEST_CONFIG_NO_MULTITHREADING
#endif

#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
#define DOCTEST_CONFIG_IMPLEMENT
#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
Expand Down Expand Up @@ -393,6 +398,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_PLATFORM_IPHONE
#elif defined(_WIN32)
#define DOCTEST_PLATFORM_WINDOWS
#elif defined(__wasi__)
#define DOCTEST_PLATFORM_WASI
#else // DOCTEST_PLATFORM
#define DOCTEST_PLATFORM_LINUX
#endif // DOCTEST_PLATFORM
Expand Down Expand Up @@ -3082,8 +3089,17 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <algorithm>
#include <iomanip>
#include <vector>
#ifndef DOCTEST_CONFIG_NO_MULTITHREADING
#include <atomic>
#include <mutex>
#define DOCTEST_DECLARE_MUTEX(name) std::mutex name;
#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name)
#define DOCTEST_LOCK_MUTEX(name) std::lock_guard<std::mutex> DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name);
#else // DOCTEST_CONFIG_NO_MULTITHREADING
#define DOCTEST_DECLARE_MUTEX(name)
#define DOCTEST_DECLARE_STATIC_MUTEX(name)
#define DOCTEST_LOCK_MUTEX(name)
#endif // DOCTEST_CONFIG_NO_MULTITHREADING
#include <set>
#include <map>
#include <exception>
Expand Down Expand Up @@ -3146,7 +3162,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#endif

#ifndef DOCTEST_THREAD_LOCAL
#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
#define DOCTEST_THREAD_LOCAL
#else // DOCTEST_MSVC
#define DOCTEST_THREAD_LOCAL thread_local
Expand Down Expand Up @@ -3339,9 +3355,17 @@ typedef timer_large_integer::type ticks_t;
ticks_t m_ticks = 0;
};

#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
#ifdef DOCTEST_CONFIG_NO_MULTITHREADING
template <typename T>
using Atomic = T;
#else // DOCTEST_CONFIG_NO_MULTITHREADING
template <typename T>
using Atomic = std::atomic<T>;
#endif // DOCTEST_CONFIG_NO_MULTITHREADING

#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
template <typename T>
using AtomicOrMultiLaneAtomic = std::atomic<T>;
using MultiLaneAtomic = Atomic<T>;
#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
// Provides a multilane implementation of an atomic variable that supports add, sub, load,
// store. Instead of using a single atomic variable, this splits up into multiple ones,
Expand All @@ -3358,8 +3382,8 @@ typedef timer_large_integer::type ticks_t;
{
struct CacheLineAlignedAtomic
{
std::atomic<T> atomic{};
char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
Atomic<T> atomic{};
char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
};
CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];

Expand Down Expand Up @@ -3415,24 +3439,21 @@ typedef timer_large_integer::type ticks_t;
// assigned in a round-robin fashion.
// 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
// little overhead.
std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
static std::atomic<size_t> laneCounter;
Atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
static Atomic<size_t> laneCounter;
DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;

return m_atomics[tlsLaneIdx].atomic;
}
};

template <typename T>
using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS

// this holds both parameters from the command line and runtime data for tests
struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
{
AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;

std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters

Expand All @@ -3449,7 +3470,7 @@ typedef timer_large_integer::type ticks_t;
std::set<decltype(subcasesStack)> subcasesPassed;
int subcasesCurrentMaxLevel;
bool should_reenter;
std::atomic<bool> shouldLogCurrentException;
Atomic<bool> shouldLogCurrentException;

void resetRunData() {
numTestCases = 0;
Expand Down Expand Up @@ -4509,10 +4530,10 @@ namespace {
static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
// Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
// console just once no matter how many threads have crashed.
static std::mutex mutex;
DOCTEST_DECLARE_STATIC_MUTEX(mutex)
static bool execute = true;
{
std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)
if(execute) {
bool reported = false;
for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
Expand Down Expand Up @@ -5271,7 +5292,7 @@ namespace {
struct XmlReporter : public IReporter
{
XmlWriter xml;
std::mutex mutex;
DOCTEST_DECLARE_MUTEX(mutex)

// caching pointers/references to objects of these types - safe to do
const ContextOptions& opt;
Expand Down Expand Up @@ -5431,7 +5452,7 @@ namespace {
}

void test_case_exception(const TestCaseException& e) override {
std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)

xml.scopedElement("Exception")
.writeAttribute("crash", e.is_crash)
Expand All @@ -5452,7 +5473,7 @@ namespace {
if(!rb.m_failed && !opt.success)
return;

std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)

xml.startElement("Expression")
.writeAttribute("success", !rb.m_failed)
Expand All @@ -5478,7 +5499,7 @@ namespace {
}

void log_message(const MessageData& mb) override {
std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)

xml.startElement("Message")
.writeAttribute("type", failureString(mb.m_severity))
Expand Down Expand Up @@ -5563,7 +5584,7 @@ namespace {
struct JUnitReporter : public IReporter
{
XmlWriter xml;
std::mutex mutex;
DOCTEST_DECLARE_MUTEX(mutex)
Timer timer;
std::vector<String> deepestSubcaseStackNames;

Expand Down Expand Up @@ -5731,7 +5752,7 @@ namespace {
}

void test_case_exception(const TestCaseException& e) override {
std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)
testCaseData.addError("exception", e.error_string.c_str());
}

Expand All @@ -5745,7 +5766,7 @@ namespace {
if(!rb.m_failed) // report only failures & ignore the `success` option
return;

std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)

std::ostringstream os;
os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
Expand Down Expand Up @@ -5796,7 +5817,7 @@ namespace {
bool hasLoggedCurrentTestStart;
std::vector<SubcaseSignature> subcasesStack;
size_t currentSubcaseLevel;
std::mutex mutex;
DOCTEST_DECLARE_MUTEX(mutex)

// caching pointers/references to objects of these types - safe to do
const ContextOptions& opt;
Expand Down Expand Up @@ -6173,7 +6194,7 @@ namespace {
}

void test_case_exception(const TestCaseException& e) override {
std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)
if(tc->m_no_output)
return;

Expand Down Expand Up @@ -6212,7 +6233,7 @@ namespace {
if((!rb.m_failed && !opt.success) || tc->m_no_output)
return;

std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)

logTestStart();

Expand All @@ -6228,7 +6249,7 @@ namespace {
if(tc->m_no_output)
return;

std::lock_guard<std::mutex> lock(mutex);
DOCTEST_LOCK_MUTEX(mutex)

logTestStart();

Expand Down
Loading

0 comments on commit 0fc371e

Please sign in to comment.