[threading linux] Implement suspend count
Add suspend count to thread implementation. Increment suspend count on suspend and decrement on resume. Wait on suspend count to be decremented to 0. Return suspend count on suspend and on resume before incr/decr. Fix naming of resume suspend count to make clear that suspend count is before incr/decr. Add test.
This commit is contained in:
parent
382dd8860f
commit
e945a13957
|
@ -849,6 +849,41 @@ TEST_CASE("Test Suspending Thread", "Thread") {
|
|||
thread->Resume();
|
||||
result = threading::Wait(thread.get(), false, 50ms);
|
||||
REQUIRE(result == threading::WaitResult::kSuccess);
|
||||
|
||||
// Test recursive suspend
|
||||
thread = threading::Thread::Create(params, func);
|
||||
thread->Suspend();
|
||||
thread->Suspend();
|
||||
result = threading::Wait(thread.get(), false, 50ms);
|
||||
REQUIRE(result == threading::WaitResult::kTimeout);
|
||||
thread->Resume();
|
||||
result = threading::Wait(thread.get(), false, 50ms);
|
||||
REQUIRE(result == threading::WaitResult::kTimeout);
|
||||
thread->Resume();
|
||||
result = threading::Wait(thread.get(), false, 50ms);
|
||||
REQUIRE(result == threading::WaitResult::kSuccess);
|
||||
|
||||
// Test suspend count
|
||||
uint32_t suspend_count = 0;
|
||||
thread = threading::Thread::Create(params, func);
|
||||
thread->Suspend(&suspend_count);
|
||||
REQUIRE(suspend_count == 0);
|
||||
thread->Suspend(&suspend_count);
|
||||
REQUIRE(suspend_count == 1);
|
||||
thread->Suspend(&suspend_count);
|
||||
REQUIRE(suspend_count == 2);
|
||||
thread->Resume(&suspend_count);
|
||||
REQUIRE(suspend_count == 3);
|
||||
thread->Resume(&suspend_count);
|
||||
REQUIRE(suspend_count == 2);
|
||||
thread->Resume(&suspend_count);
|
||||
REQUIRE(suspend_count == 1);
|
||||
thread->Suspend(&suspend_count);
|
||||
REQUIRE(suspend_count == 0);
|
||||
thread->Resume(&suspend_count);
|
||||
REQUIRE(suspend_count == 1);
|
||||
result = threading::Wait(thread.get(), false, 50ms);
|
||||
REQUIRE(result == threading::WaitResult::kSuccess);
|
||||
}
|
||||
|
||||
TEST_CASE("Test Thread QueueUserCallback", "Thread") {
|
||||
|
|
|
@ -389,7 +389,7 @@ class Thread : public WaitHandle {
|
|||
|
||||
// Decrements a thread's suspend count. When the suspend count is decremented
|
||||
// to zero, the execution of the thread is resumed.
|
||||
virtual bool Resume(uint32_t* out_new_suspend_count = nullptr) = 0;
|
||||
virtual bool Resume(uint32_t* out_previous_suspend_count = nullptr) = 0;
|
||||
|
||||
// Suspends the specified thread.
|
||||
virtual bool Suspend(uint32_t* out_previous_suspend_count = nullptr) = 0;
|
||||
|
|
|
@ -473,7 +473,8 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
|||
: thread_(0),
|
||||
signaled_(false),
|
||||
exit_code_(0),
|
||||
state_(State::kUninitialized) {}
|
||||
state_(State::kUninitialized),
|
||||
suspend_count_(0) {}
|
||||
bool Initialize(Thread::CreationParameters params,
|
||||
ThreadStartData* start_data) {
|
||||
start_data->create_suspended = params.create_suspended;
|
||||
|
@ -608,21 +609,33 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
|||
user_callback_();
|
||||
}
|
||||
|
||||
bool Resume(uint32_t* out_new_suspend_count = nullptr) {
|
||||
// TODO(bwrsandman): implement suspend_count
|
||||
assert_null(out_new_suspend_count);
|
||||
bool Resume(uint32_t* out_previous_suspend_count = nullptr) {
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = 0;
|
||||
}
|
||||
WaitStarted();
|
||||
std::unique_lock<std::mutex> lock(state_mutex_);
|
||||
if (state_ != State::kSuspended) return false;
|
||||
state_ = State::kRunning;
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = suspend_count_;
|
||||
}
|
||||
--suspend_count_;
|
||||
state_signal_.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Suspend(uint32_t* out_previous_suspend_count = nullptr) {
|
||||
// TODO(bwrsandman): implement suspend_count
|
||||
assert_null(out_previous_suspend_count);
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = 0;
|
||||
}
|
||||
WaitStarted();
|
||||
{
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = suspend_count_;
|
||||
}
|
||||
state_ = State::kSuspended;
|
||||
++suspend_count_;
|
||||
}
|
||||
int result =
|
||||
pthread_kill(thread_, GetSystemSignal(SignalType::kThreadSuspend));
|
||||
return result == 0;
|
||||
|
@ -656,8 +669,8 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
|||
/// Set state to suspended and wait until it reset by another thread
|
||||
void WaitSuspended() {
|
||||
std::unique_lock<std::mutex> lock(state_mutex_);
|
||||
state_ = State::kSuspended;
|
||||
state_signal_.wait(lock, [this] { return state_ != State::kSuspended; });
|
||||
state_signal_.wait(lock, [this] { return suspend_count_ == 0; });
|
||||
state_ = State::kRunning;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -673,6 +686,7 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
|||
bool signaled_;
|
||||
int exit_code_;
|
||||
volatile State state_;
|
||||
volatile uint32_t suspend_count_;
|
||||
mutable std::mutex state_mutex_;
|
||||
mutable std::mutex callback_mutex_;
|
||||
mutable std::condition_variable state_signal_;
|
||||
|
@ -883,8 +897,8 @@ class PosixThread : public PosixConditionHandle<Thread> {
|
|||
handle_.QueueUserCallback(std::move(callback));
|
||||
}
|
||||
|
||||
bool Resume(uint32_t* out_new_suspend_count) override {
|
||||
return handle_.Resume(out_new_suspend_count);
|
||||
bool Resume(uint32_t* out_previous_suspend_count) override {
|
||||
return handle_.Resume(out_previous_suspend_count);
|
||||
}
|
||||
|
||||
bool Suspend(uint32_t* out_previous_suspend_count) override {
|
||||
|
@ -923,8 +937,9 @@ void* PosixCondition<Thread>::ThreadStartRoutine(void* parameter) {
|
|||
|
||||
if (create_suspended) {
|
||||
std::unique_lock<std::mutex> lock(thread->handle_.state_mutex_);
|
||||
thread->handle_.suspend_count_ = 1;
|
||||
thread->handle_.state_signal_.wait(
|
||||
lock, [thread] { return thread->handle_.state_ != State::kSuspended; });
|
||||
lock, [thread] { return thread->handle_.suspend_count_ == 0; });
|
||||
}
|
||||
|
||||
start_routine();
|
||||
|
|
|
@ -388,16 +388,16 @@ class Win32Thread : public Win32Handle<Thread> {
|
|||
QueueUserAPC(DispatchApc, handle_, reinterpret_cast<ULONG_PTR>(apc_data));
|
||||
}
|
||||
|
||||
bool Resume(uint32_t* out_new_suspend_count = nullptr) override {
|
||||
if (out_new_suspend_count) {
|
||||
*out_new_suspend_count = 0;
|
||||
bool Resume(uint32_t* out_previous_suspend_count = nullptr) override {
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = 0;
|
||||
}
|
||||
DWORD result = ResumeThread(handle_);
|
||||
if (result == UINT_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (out_new_suspend_count) {
|
||||
*out_new_suspend_count = result;
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue