Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions cxplat/cxplat_test/cxplat_fault_injection_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,21 @@ TEST_CASE("fault_injection", "[fault_injection]")
// Clear the fault injection state.
cxplat_fault_injection_reset();

auto inject_fault_from_same_callsite = []() { return cxplat_fault_injection_inject_fault(); };

cxplat_fault_injection_suspend();
REQUIRE(cxplat_fault_injection_is_enabled() == true);
REQUIRE(inject_fault_from_same_callsite() == false);

cxplat_fault_injection_suspend();
cxplat_fault_injection_resume();
REQUIRE(inject_fault_from_same_callsite() == false);

cxplat_fault_injection_resume();
REQUIRE(inject_fault_from_same_callsite() == true);

cxplat_fault_injection_reset();

for (_fault_injection_expected_outcome state = _fault_injection_expected_outcome::ExpectFault;
state <= _fault_injection_expected_outcome::ExpectFaultDifferentCallsite;
state = (_fault_injection_expected_outcome)((int)state + 1)) {
Expand Down
16 changes: 16 additions & 0 deletions cxplat/inc/winuser/cxplat_fault_injection.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#ifndef CXPLAT_DEBUGGING_FEATURES_ENABLED
#define cxplat_fault_injection_is_enabled() false
#define cxplat_fault_injection_inject_fault() false
#define cxplat_fault_injection_suspend() ((void)0)
#define cxplat_fault_injection_resume() ((void)0)
#else
#include <stdbool.h>

Expand Down Expand Up @@ -50,6 +52,20 @@ cxplat_fault_injection_inject_fault() CXPLAT_NOEXCEPT;
bool
cxplat_fault_injection_is_enabled() CXPLAT_NOEXCEPT;

/**
* @brief Suspend fault injection. Calls may be nested and must be matched by calls to
* cxplat_fault_injection_resume(). This function is thread safe.
*/
void
cxplat_fault_injection_suspend() CXPLAT_NOEXCEPT;

/**
* @brief Resume fault injection after a matching call to cxplat_fault_injection_suspend().
* This function is thread safe.
*/
void
cxplat_fault_injection_resume() CXPLAT_NOEXCEPT;

/**
* @brief Reset fault injection. This function is thread safe.
*/
Expand Down
47 changes: 47 additions & 0 deletions cxplat/src/cxplat_winuser/fault_injection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <map>
#include <mutex>
#include <psapi.h>
#include <shared_mutex>
#include <sstream>
#include <string>
#include <tuple>
Expand Down Expand Up @@ -40,6 +41,12 @@ typedef class _cxplat_fault_injection
bool
inject_fault();

void
suspend();

void
resume();

/**
* @brief Reset the fault injection state, both in memory and on disk.
*/
Expand Down Expand Up @@ -139,6 +146,9 @@ typedef class _cxplat_fault_injection
*/
std::mutex _mutex;

std::shared_mutex _suspension_mutex;
_Guarded_by_(_suspension_mutex) size_t _suspension_count = 0;

size_t _stack_depth;
_Guarded_by_(_mutex) std::vector<std::string> _last_fault_stack;

Expand Down Expand Up @@ -209,9 +219,30 @@ _cxplat_fault_injection::~_cxplat_fault_injection()
bool
_cxplat_fault_injection::inject_fault()
{
std::shared_lock lock(_suspension_mutex);
if (_suspension_count > 0) {
return false;
}
return is_new_stack();
}

void
_cxplat_fault_injection::suspend()
{
std::unique_lock lock(_suspension_mutex);
_suspension_count++;
}

void
_cxplat_fault_injection::resume()
{
std::unique_lock lock(_suspension_mutex);
CXPLAT_RUNTIME_ASSERT(_suspension_count > 0);
if (_suspension_count > 0) {
_suspension_count--;
}
}

void
_cxplat_fault_injection::reset()
{
Expand Down Expand Up @@ -419,6 +450,22 @@ cxplat_fault_injection_is_enabled() noexcept
return _cxplat_fault_injection_singleton != nullptr;
}

void
cxplat_fault_injection_suspend() noexcept
{
if (_cxplat_fault_injection_singleton) {
_cxplat_fault_injection_singleton->suspend();
}
}

void
cxplat_fault_injection_resume() noexcept
{
if (_cxplat_fault_injection_singleton) {
_cxplat_fault_injection_singleton->resume();
}
}

void
cxplat_fault_injection_reset() noexcept
{
Expand Down