-
Notifications
You must be signed in to change notification settings - Fork 2
KF-30 add tests for EResource #62
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,7 +53,7 @@ namespace kf | |
| } | ||
|
|
||
| private: | ||
| Thread m_threads[kMaxCount]; | ||
| Thread m_threads[kMaxCount]{}; | ||
| int m_count; | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,224 @@ | ||
| #include "pch.h" | ||
| #include <kf/EResource.h> | ||
| #include <kf/ThreadPool.h> | ||
|
|
||
| namespace | ||
| { | ||
| constexpr int kOneMillisecond = 10'000; // 1 ms in 100-nanosecond intervals | ||
|
|
||
| void delay() | ||
| { | ||
| LARGE_INTEGER interval; | ||
| interval.QuadPart = -kOneMillisecond; | ||
| KeDelayExecutionThread(KernelMode, FALSE, &interval); | ||
| } | ||
| } | ||
|
|
||
| SCENARIO("kf::EResource") | ||
| { | ||
| GIVEN("An EResource object") | ||
| { | ||
| kf::EResource resource; | ||
|
|
||
| THEN("The resource is initialized and not acquired immediately") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
|
|
||
| GIVEN("The EResource acquired exclusive") | ||
| { | ||
| kf::EResource resource; | ||
| bool acquired = resource.acquireExclusive(); | ||
|
|
||
| WHEN("The resource is acquired exclusively") | ||
| { | ||
| //The system considers exclusive access to be a subset of shared access. | ||
| //Therefore, a thread that has exclusive access to a resource also has shared access to the resource. | ||
| THEN("The exclusive and shared locks are acquired") | ||
| { | ||
| REQUIRE(acquired == true); | ||
| REQUIRE(resource.isAcquiredExclusive() == true); | ||
| REQUIRE(resource.isAcquiredShared() == 1); | ||
| } | ||
|
|
||
| WHEN("The exclusive lock is released") | ||
| { | ||
| resource.release(); | ||
|
|
||
| THEN("The resource is no longer locked") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| GIVEN("The EResource is acquired shared") | ||
| { | ||
| kf::EResource resource; | ||
| bool acquired = resource.acquireShared(); | ||
|
|
||
| THEN("The only shared lock is acquired") | ||
| { | ||
| REQUIRE(acquired == true); | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 1); | ||
| } | ||
|
|
||
| WHEN("The shared lock is released") | ||
| { | ||
| resource.release(); | ||
|
|
||
| THEN("The resource is no longer locked") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| GIVEN("The EResource acquired exclusive") | ||
| { | ||
| kf::EResource resource; | ||
| bool exclusiveAcquired = resource.acquireExclusive(); | ||
| REQUIRE(exclusiveAcquired == true); | ||
|
|
||
| WHEN("The resource is converted to shared") | ||
| { | ||
| resource.convertExclusiveToShared(); | ||
belyshevdenis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| THEN("The lock is converted to shared") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 1); | ||
| } | ||
|
|
||
| WHEN("The shared lock is released") | ||
| { | ||
| resource.release(); | ||
|
|
||
| THEN("The resource is no longer locked") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| GIVEN("The EResource locked using the lock function") | ||
| { | ||
| kf::EResource resource; | ||
| resource.lock(); | ||
|
|
||
| THEN("The resource is acquired exclusively and shared") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == true); | ||
| REQUIRE(resource.isAcquiredShared() == 1); | ||
| } | ||
|
|
||
| WHEN("The resource is unlocked") | ||
| { | ||
| resource.unlock(); | ||
|
|
||
| THEN("The resource is no longer locked") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| GIVEN("The EResource is locked shared using the lock_shared function") | ||
| { | ||
| kf::EResource resource; | ||
| resource.lock_shared(); | ||
|
|
||
| THEN("The resource is acquired shared") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 1); | ||
| } | ||
|
|
||
| WHEN("The resource is unlocked shared") | ||
| { | ||
| resource.unlock_shared(); | ||
|
|
||
| THEN("The resource is no longer locked") | ||
| { | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| GIVEN("The EResource and multiple threads") | ||
| { | ||
| constexpr int kMaxThreadsCount = 64; | ||
| const ULONG numLogicalProcessors = KeQueryActiveProcessorCount(nullptr); | ||
| REQUIRE(numLogicalProcessors <= kMaxThreadsCount); | ||
|
|
||
| struct Context | ||
| { | ||
| kf::EResource* resource = nullptr; | ||
| std::array<bool, kMaxThreadsCount>* acquired = nullptr; | ||
| LONG counter = 0; | ||
| }; | ||
| kf::EResource resource; | ||
| std::array<bool, kMaxThreadsCount> acquired; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check that numLogicalProcessors <= kMaxThreadsCount
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. KeQueryActiveProcessorCount returns a number of processors for one processor group, which is a static set of up to 64 logical processors. So, the return value can never be greater than 64. |
||
| Context context{ &resource, &acquired }; | ||
| kf::ThreadPool threadPool(numLogicalProcessors); | ||
|
|
||
| WHEN("Multiple threads attempt to acquire exclusive the resource") | ||
| { | ||
| threadPool.start([](void* context) { | ||
| auto res = static_cast<Context*>(context); | ||
| res->resource->acquireExclusive(); | ||
| res->acquired->at(res->counter++) = true; | ||
| delay(); | ||
| res->resource->release(); | ||
| }, &context); | ||
|
|
||
| threadPool.join(); | ||
|
|
||
| THEN("All threads can acquire and release the resource without deadlock") | ||
| { | ||
| auto size = min(kMaxThreadsCount, numLogicalProcessors); | ||
| for (size_t i = 0; i < size; i++) | ||
| { | ||
| REQUIRE(acquired.at(i) == true); | ||
| } | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
|
|
||
| WHEN("Multiple threads attempt to acquire shared the resource") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a better way to check that shared access was successfull without waiting. Create a hundred of threads that read some resource and register this event, then join them all and then check that every consumer got what it wanted
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added ThreadPool with maximum count of logical threads |
||
| { | ||
| threadPool.start([](void* context) { | ||
| auto res = static_cast<Context*>(context); | ||
| res->resource->acquireShared(); | ||
| auto index = InterlockedIncrement(&res->counter) - 1; | ||
| res->acquired->at(index) = true; | ||
| delay(); | ||
| res->resource->release(); | ||
| }, &context); | ||
|
|
||
| threadPool.join(); | ||
|
|
||
| THEN("All threads can acquire and release the resource without deadlock") | ||
| { | ||
| auto size = min(kMaxThreadsCount, numLogicalProcessors); | ||
| for (size_t i = 0; i < size; i++) | ||
| { | ||
| REQUIRE(acquired.at(i) == true); | ||
| } | ||
| REQUIRE(resource.isAcquiredExclusive() == false); | ||
| REQUIRE(resource.isAcquiredShared() == 0); | ||
| } | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.