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
4 changes: 2 additions & 2 deletions include/kf/Semaphore.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ namespace kf
return &m_semaphore;
}

NTSTATUS wait(_In_opt_ PLARGE_INTEGER timeout = nullptr)
NTSTATUS wait(_In_opt_ const LARGE_INTEGER* timeout = nullptr)
{
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
return KeWaitForSingleObject(&m_semaphore, Executive, KernelMode, false, timeout);
return KeWaitForSingleObject(&m_semaphore, Executive, KernelMode, false, const_cast<LARGE_INTEGER*>(timeout));
}

void release(LONG adjustment = 1)
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ wdk_add_driver(kf-test WINVER NTDDI_WIN10 STL
AutoSpinLockTest.cpp
EResourceSharedLockTest.cpp
RecursiveAutoSpinLockTest.cpp
SemaphoreTest.cpp
UStringBuilderTest.cpp
USimpleStringTest.cpp
)
Expand Down
132 changes: 132 additions & 0 deletions test/SemaphoreTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#include "pch.h"
#include <kf/Semaphore.h>

SCENARIO("kf::Semaphore")
{
constexpr LARGE_INTEGER kOneMillisecond{ .QuadPart = -10'000LL }; // 1ms
constexpr LARGE_INTEGER kZeroTimeout{};

GIVEN("A semaphore with count = 1 and limit = 1")
{
kf::Semaphore sem(1, 1);

WHEN("Trying to acquire should succeed immediately")
{
THEN("Thread doesn't wait")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
}
}

WHEN("Acquiring twice should deplete the count and then timeout")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));

THEN("Second wait times out")
{
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));
}
}

WHEN("After depleting, release allows acquiring again")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));

sem.release();

THEN("Wait succeeds after release")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
}
}
}

GIVEN("A semaphore with count = 0 and limit = 1")
{
kf::Semaphore sem(0, 1);

WHEN("Trying to acquire should timeout")
{
THEN("Wait fails with timeout")
{
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));
}
}

WHEN("Release makes next acquire succeed and then timeout again")
{
sem.release();

THEN("First wait succeeds immediately and subsequent wait times out")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));
}
}
}

GIVEN("A semaphore with count = 1000 and limit = 1000")
{
kf::Semaphore sem(1000, 1000);

WHEN("Acquiring up to count succeeds, extra wait times out")
{
for (int i = 0; i < 1000; ++i)
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
}

THEN("Next wait times out")
{
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));
}
}

WHEN("Releasing after depletion allows acquire to succeed")
{
for (int i = 0; i < 1000; ++i)
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
}
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));

sem.release();

THEN("Wait succeeds after release")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
}
}
}

GIVEN("A semaphore with count = 0, limit = 2 and multi-adjust release")
{
kf::Semaphore sem(0, 2);

WHEN("Release with adjustment 2")
{
sem.release(2);

THEN("Two immediate acquires succeed, third times out")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));
}
}

WHEN("Multiple single releases allow multiple acquires")
{
sem.release();
sem.release();

THEN("Two immediate acquires succeed, next times out")
{
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
REQUIRE(STATUS_SUCCESS == sem.wait(&kZeroTimeout));
REQUIRE(STATUS_TIMEOUT == sem.wait(&kOneMillisecond));
}
}
}
}