Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulate EX_RUNDOWN_REF_CACHE_AWARE APIS #181

Merged
merged 1 commit into from
Apr 4, 2024
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
24 changes: 24 additions & 0 deletions inc/usersim/ex.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ typedef struct _EX_SPIN_LOCK
{
SRWLOCK lock;
} EX_SPIN_LOCK;

typedef cxplat_rundown_reference_t EX_RUNDOWN_REF;

// Initial usersim version of this API will not be cache aware and will use the same type as EX_RUNDOWN_REF.
typedef cxplat_rundown_reference_t EX_RUNDOWN_REF_CACHE_AWARE;

//
// Pool Allocation routines (in pool.c)
//
Expand Down Expand Up @@ -104,6 +108,26 @@ USERSIM_API
void
ExReleaseRundownProtection(_Inout_ EX_RUNDOWN_REF* rundown_ref);

USERSIM_API
EX_RUNDOWN_REF_CACHE_AWARE*
ExAllocateCacheAwareRundownProtection(
_In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType, unsigned long PoolTag);

USERSIM_API
BOOLEAN ExAcquireRundownProtectionCacheAware(
_Inout_ EX_RUNDOWN_REF_CACHE_AWARE* RunRefCacheAware
);

USERSIM_API
void ExReleaseRundownProtectionCacheAware(
_Inout_ EX_RUNDOWN_REF_CACHE_AWARE* RunRefCacheAware
);

USERSIM_API
void ExFreeCacheAwareRundownProtection(
_Inout_ EX_RUNDOWN_REF_CACHE_AWARE* RunRefCacheAware
);

USERSIM_API
_Acquires_exclusive_lock_(push_lock->lock) void ExAcquirePushLockExclusiveEx(
_Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_lock_(*_Curr_) EX_PUSH_LOCK* push_lock,
Expand Down
31 changes: 31 additions & 0 deletions src/ex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ ExReleaseRundownProtection(_Inout_ EX_RUNDOWN_REF* rundown_reference)
cxplat_release_rundown_protection(rundown_reference);
}

EX_RUNDOWN_REF_CACHE_AWARE*
ExAllocateCacheAwareRundownProtection(
_In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType, unsigned long PoolTag)
{
EX_RUNDOWN_REF_CACHE_AWARE* rundown_reference =
(EX_RUNDOWN_REF_CACHE_AWARE*)ExAllocatePoolWithTag(PoolType, sizeof(EX_RUNDOWN_REF_CACHE_AWARE), PoolTag);

if (rundown_reference != nullptr) {
cxplat_initialize_rundown_protection(rundown_reference);
}
return rundown_reference;
}

BOOLEAN
ExAcquireRundownProtectionCacheAware(_Inout_ EX_RUNDOWN_REF_CACHE_AWARE* RunRefCacheAware)
{
return (BOOLEAN)cxplat_acquire_rundown_protection(RunRefCacheAware);
}

void
ExReleaseRundownProtectionCacheAware(_Inout_ EX_RUNDOWN_REF_CACHE_AWARE* RunRefCacheAware)
{
cxplat_release_rundown_protection(RunRefCacheAware);
}

void
ExFreeCacheAwareRundownProtection(_Inout_ EX_RUNDOWN_REF_CACHE_AWARE* RunRefCacheAware)
{
ExFreePool(RunRefCacheAware);
}

_Acquires_exclusive_lock_(push_lock->lock) void ExAcquirePushLockExclusiveEx(
_Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_lock_(*_Curr_) EX_PUSH_LOCK* push_lock,
_In_ unsigned long flags)
Expand Down
111 changes: 111 additions & 0 deletions tests/ex_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <catch2/catch.hpp>
#endif
#include "usersim/ex.h"
#include "cxplat_winuser.h"

#include <thread>

TEST_CASE("ExAllocatePool", "[ex]")
{
Expand Down Expand Up @@ -116,4 +119,112 @@ TEST_CASE("ExRaiseDatatypeMisalignment", "[ex]")
int64_t code = _atoi64(ex);
REQUIRE(code == STATUS_DATATYPE_MISALIGNMENT);
}
}

TEST_CASE("EX_RUNDOWN_REF", "[ex]")
{
EX_RUNDOWN_REF ref;
std::atomic<bool> thread_completed = false;
ExInitializeRundownProtection(&ref);

// Acquire before rundown is initiated.
// Acquire the first rundown protection reference.
REQUIRE(ExAcquireRundownProtection(&ref));

// Acquire the second rundown protection reference.
REQUIRE(ExAcquireRundownProtection(&ref));

// Wait for the rundown protection to be released.
std::thread thread([&]() {
// Wait for the rundown protection to be released.
ExWaitForRundownProtectionRelease(&ref);
thread_completed = true;
});

std::this_thread::sleep_for(std::chrono::milliseconds(100));

// Thread should be waiting for the rundown protection to be released.
REQUIRE(!thread_completed);

// Acquire after rundown is initiated.
// Future acquire of the rundown protection should fail.
REQUIRE(!ExAcquireRundownProtection(&ref));

// Release the second rundown protection reference.
ExReleaseRundownProtection(&ref);

std::this_thread::sleep_for(std::chrono::milliseconds(100));

// Thread should be waiting for the rundown protection to be released.
REQUIRE(!thread_completed);

// Release the first rundown protection reference.
ExReleaseRundownProtection(&ref);

// Thread should have completed.
std::this_thread::sleep_for(std::chrono::milliseconds(100));

thread.join();

// Thread should be waiting for the rundown protection to be released.
REQUIRE(thread_completed);

// Acquire after rundown is completed.

// Future acquire of the rundown protection should fail.
REQUIRE(!ExAcquireRundownProtection(&ref));
}

TEST_CASE("EX_RUNDOWN_REF_CACHE_AWARE", "[ex]")
{
EX_RUNDOWN_REF_CACHE_AWARE* ref = ExAllocateCacheAwareRundownProtection(NonPagedPoolNx, 'tset');
std::atomic<bool> thread_completed = false;
REQUIRE(ref != nullptr);

// Acquire before rundown is initiated.
// Acquire the first rundown protection reference.
REQUIRE(ExAcquireRundownProtectionCacheAware(ref));

// Acquire the second rundown protection reference.
REQUIRE(ExAcquireRundownProtectionCacheAware(ref));

// Wait for the rundown protection to be released.
std::thread thread([&]() {
// Wait for the rundown protection to be released.
ExWaitForRundownProtectionRelease(ref);
thread_completed = true;
});

std::this_thread::sleep_for(std::chrono::milliseconds(100));

// Thread should be waiting for the rundown protection to be released.
REQUIRE(!thread_completed);

// Acquire after rundown is initiated.
// Future acquire of the rundown protection should fail.
REQUIRE(!ExAcquireRundownProtectionCacheAware(ref));

// Release the second rundown protection reference.
ExReleaseRundownProtectionCacheAware(ref);

std::this_thread::sleep_for(std::chrono::milliseconds(100));

// Thread should be waiting for the rundown protection to be released.
REQUIRE(!thread_completed);

// Release the first rundown protection reference.
ExReleaseRundownProtectionCacheAware(ref);

// Thread should have completed.
std::this_thread::sleep_for(std::chrono::milliseconds(100));

thread.join();
REQUIRE(thread_completed);

// Acquire after rundown is completed.

// Future acquire of the rundown protection should fail.
REQUIRE(!ExAcquireRundownProtectionCacheAware(ref));

ExFreeCacheAwareRundownProtection(ref);
}