[threading linux] Implement Mutant

Keep track of recursive locks with owner and count of locks.
Only allow recursive locks from same thread and increment count.
Only allow first locks from when count is zero.

Test acquiring and releasing mutant on same and on different threads.
Test Release return values.
Test WaitAll and WaitAny.
This commit is contained in:
Sandy Carter 2018-12-09 15:44:44 -08:00 committed by Rick Gibbed
parent 5d0efedaf4
commit 331bb0ea9a
2 changed files with 158 additions and 14 deletions

View File

@ -433,8 +433,123 @@ TEST_CASE("Wait on Multiple Semaphores", "Semaphore") {
}
TEST_CASE("Wait on Mutant", "Mutant") {
// TODO(bwrsandman):
REQUIRE(true);
WaitResult result;
std::unique_ptr<Mutant> mut;
// Release on initially owned mutant
mut = Mutant::Create(true);
REQUIRE(mut->Release());
REQUIRE_FALSE(mut->Release());
// Release on initially not-owned mutant
mut = Mutant::Create(false);
REQUIRE_FALSE(mut->Release());
// Wait on initially owned mutant
mut = Mutant::Create(true);
result = Wait(mut.get(), false, 1ms);
REQUIRE(result == WaitResult::kSuccess);
REQUIRE(mut->Release());
REQUIRE(mut->Release());
REQUIRE_FALSE(mut->Release());
// Wait on initially not owned mutant
mut = Mutant::Create(false);
result = Wait(mut.get(), false, 1ms);
REQUIRE(result == WaitResult::kSuccess);
REQUIRE(mut->Release());
REQUIRE_FALSE(mut->Release());
// Multiple waits (or locks)
mut = Mutant::Create(false);
for (int i = 0; i < 10; ++i) {
result = Wait(mut.get(), false, 1ms);
REQUIRE(result == WaitResult::kSuccess);
}
for (int i = 0; i < 10; ++i) {
REQUIRE(mut->Release());
}
REQUIRE_FALSE(mut->Release());
// Test mutants on other threads
auto thread1 = std::thread([&mut] {
Sleep(5ms);
mut = Mutant::Create(true);
Sleep(100ms);
mut->Release();
});
Sleep(10ms);
REQUIRE_FALSE(mut->Release());
Sleep(10ms);
result = Wait(mut.get(), false, 50ms);
REQUIRE(result == WaitResult::kTimeout);
thread1.join();
result = Wait(mut.get(), false, 1ms);
REQUIRE(result == WaitResult::kSuccess);
REQUIRE(mut->Release());
}
TEST_CASE("Wait on Multiple Mutants", "Mutant") {
WaitResult all_result;
std::pair<WaitResult, size_t> any_result;
std::unique_ptr<Mutant> mut0, mut1;
// Test which should fail for WaitAll and WaitAny
auto thread0 = std::thread([&mut0, &mut1] {
mut0 = Mutant::Create(true);
mut1 = Mutant::Create(true);
Sleep(50ms);
mut0->Release();
mut1->Release();
});
Sleep(10ms);
all_result = WaitAll({mut0.get(), mut1.get()}, false, 10ms);
REQUIRE(all_result == WaitResult::kTimeout);
REQUIRE_FALSE(mut0->Release());
REQUIRE_FALSE(mut1->Release());
any_result = WaitAny({mut0.get(), mut1.get()}, false, 10ms);
REQUIRE(any_result.first == WaitResult::kTimeout);
REQUIRE(any_result.second == 0);
REQUIRE_FALSE(mut0->Release());
REQUIRE_FALSE(mut1->Release());
thread0.join();
// Test which should fail for WaitAll but not WaitAny
auto thread1 = std::thread([&mut0, &mut1] {
mut0 = Mutant::Create(true);
mut1 = Mutant::Create(false);
Sleep(50ms);
mut0->Release();
});
Sleep(10ms);
all_result = WaitAll({mut0.get(), mut1.get()}, false, 10ms);
REQUIRE(all_result == WaitResult::kTimeout);
REQUIRE_FALSE(mut0->Release());
REQUIRE_FALSE(mut1->Release());
any_result = WaitAny({mut0.get(), mut1.get()}, false, 10ms);
REQUIRE(any_result.first == WaitResult::kSuccess);
REQUIRE(any_result.second == 1);
REQUIRE_FALSE(mut0->Release());
REQUIRE(mut1->Release());
thread1.join();
// Test which should pass for WaitAll and WaitAny
auto thread2 = std::thread([&mut0, &mut1] {
mut0 = Mutant::Create(false);
mut1 = Mutant::Create(false);
Sleep(50ms);
});
Sleep(10ms);
all_result = WaitAll({mut0.get(), mut1.get()}, false, 10ms);
REQUIRE(all_result == WaitResult::kSuccess);
REQUIRE(mut0->Release());
REQUIRE(mut1->Release());
any_result = WaitAny({mut0.get(), mut1.get()}, false, 10ms);
REQUIRE(any_result.first == WaitResult::kSuccess);
REQUIRE(any_result.second == 0);
REQUIRE(mut0->Release());
REQUIRE_FALSE(mut1->Release());
thread2.join();
}
TEST_CASE("Create and Trigger Timer", "Timer") {

View File

@ -317,6 +317,40 @@ class PosixCondition<Semaphore> : public PosixConditionBase {
const uint32_t maximum_count_;
};
template <>
class PosixCondition<Mutant> : public PosixConditionBase {
public:
explicit PosixCondition(bool initial_owner) : count_(0) {
if (initial_owner) {
count_ = 1;
owner_ = std::this_thread::get_id();
}
}
bool Release() {
if (owner_ == std::this_thread::get_id() && count_ > 0) {
auto lock = std::unique_lock<std::mutex>(mutex_);
--count_;
// Free to be acquired by another thread
if (count_ == 0) {
cond_.notify_one();
}
return true;
}
return false;
}
private:
inline bool signaled() const override {
return count_ == 0 || owner_ == std::this_thread::get_id();
}
inline void post_execution() override {
count_++;
owner_ = std::this_thread::get_id();
}
uint32_t count_;
std::thread::id owner_;
};
// Native posix thread handle
template <typename T>
class PosixThreadHandle : public T {
@ -337,6 +371,7 @@ class PosixThreadHandle : public T {
template <typename T>
class PosixConditionHandle : public T {
public:
explicit PosixConditionHandle(bool initial_owner);
PosixConditionHandle(bool manual_reset, bool initial_state);
PosixConditionHandle(uint32_t initial_count, uint32_t maximum_count);
~PosixConditionHandle() override = default;
@ -355,9 +390,8 @@ PosixConditionHandle<Semaphore>::PosixConditionHandle(uint32_t initial_count,
: handle_(initial_count, maximum_count) {}
template <>
PosixConditionHandle<Mutant>::PosixConditionHandle(bool manual_reset,
bool initial_state)
: handle_() {}
PosixConditionHandle<Mutant>::PosixConditionHandle(bool initial_owner)
: handle_(initial_owner) {}
template <>
PosixConditionHandle<Timer>::PosixConditionHandle(bool manual_reset,
@ -442,17 +476,12 @@ std::unique_ptr<Semaphore> Semaphore::Create(int initial_count,
return std::make_unique<PosixSemaphore>(initial_count, maximum_count);
}
// TODO(dougvj)
class PosixMutant : public PosixConditionHandle<Mutant> {
public:
PosixMutant(bool initial_owner) : PosixConditionHandle(false, false) {
assert_always();
}
~PosixMutant() = default;
bool Release() override {
assert_always();
return false;
}
explicit PosixMutant(bool initial_owner)
: PosixConditionHandle(initial_owner) {}
~PosixMutant() override = default;
bool Release() override { return handle_.Release(); }
};
std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) {