[threading linux] Implement Callback Queuing

Add thread local bool for alertable state.
Use real-time event interrupt to run callback.
Fix sleep duration from miliseconds (microseconds / 1000) to seconds in sleep
command.
Add note for future implementation.

Ignore real-time event 37 in .gdbinit which is used to signal callback.

Test AlertableSleep
Test Thread QueueUserCallback.
TODO: Test alerted wait result when using IO functions.
This commit is contained in:
Sandy Carter
2018-03-11 14:48:55 -04:00
committed by Rick Gibbed
parent 4397f25325
commit 634f87f63b
3 changed files with 128 additions and 14 deletions

View File

@@ -38,7 +38,13 @@ inline timespec DurationToTimeSpec(
// This implementation uses the SIGRTMAX - SIGRTMIN to signal to a thread
// gdb tip, for SIG = SIGRTMIN + SignalType : handle SIG nostop
// lldb tip, for SIG = SIGRTMIN + SignalType : process handle SIG -s false
enum class SignalType { kHighResolutionTimer, kTimer, kThreadSuspend, k_Count };
enum class SignalType {
kHighResolutionTimer,
kTimer,
kThreadSuspend,
kThreadUserCallback,
k_Count
};
int GetSystemSignal(SignalType num) {
auto result = SIGRTMIN + static_cast<int>(num);
@@ -102,9 +108,12 @@ void Sleep(std::chrono::microseconds duration) {
} while (ret == -1 && errno == EINTR);
}
// TODO(dougvj) Not sure how to implement the equivalent of this on POSIX.
// TODO(bwrsandman) Implement by allowing alert interrupts from IO operations
thread_local bool alertable_state_ = false;
SleepResult AlertableSleep(std::chrono::microseconds duration) {
sleep(duration.count() / 1000);
alertable_state_ = true;
Sleep(duration);
alertable_state_ = false;
return SleepResult::kSuccess;
}
@@ -567,8 +576,18 @@ class PosixCondition<Thread> : public PosixConditionBase {
}
void QueueUserCallback(std::function<void()> callback) {
// TODO(bwrsandman)
assert_always();
WaitStarted();
std::unique_lock<std::mutex> lock(callback_mutex_);
user_callback_ = std::move(callback);
sigval value{};
value.sival_ptr = this;
pthread_sigqueue(thread_, GetSystemSignal(SignalType::kThreadUserCallback),
value);
}
void CallUserCallback() {
std::unique_lock<std::mutex> lock(callback_mutex_);
user_callback_();
}
bool Resume(uint32_t* out_new_suspend_count = nullptr) {
@@ -637,7 +656,9 @@ class PosixCondition<Thread> : public PosixConditionBase {
int exit_code_;
volatile State state_;
mutable std::mutex state_mutex_;
mutable std::mutex callback_mutex_;
mutable std::condition_variable state_signal_;
std::function<void()> user_callback_;
};
// This wraps a condition object as our handle because posix has no single
@@ -687,7 +708,10 @@ WaitResult Wait(WaitHandle* wait_handle, bool is_alertable,
std::chrono::milliseconds timeout) {
auto handle =
reinterpret_cast<PosixConditionBase*>(wait_handle->native_handle());
return handle->Wait(timeout);
if (is_alertable) alertable_state_ = true;
auto result = handle->Wait(timeout);
if (is_alertable) alertable_state_ = false;
return result;
}
// TODO(dougvj)
@@ -695,10 +719,12 @@ WaitResult SignalAndWait(WaitHandle* wait_handle_to_signal,
WaitHandle* wait_handle_to_wait_on, bool is_alertable,
std::chrono::milliseconds timeout) {
assert_always();
return WaitResult::kFailed;
if (is_alertable) alertable_state_ = true;
auto result = WaitResult::kFailed;
if (is_alertable) alertable_state_ = false;
return result;
}
// TODO(bwrsandman): Add support for is_alertable
std::pair<WaitResult, size_t> WaitMultiple(WaitHandle* wait_handles[],
size_t wait_handle_count,
bool wait_all, bool is_alertable,
@@ -708,8 +734,11 @@ std::pair<WaitResult, size_t> WaitMultiple(WaitHandle* wait_handles[],
handles[i] =
reinterpret_cast<PosixConditionBase*>(wait_handles[i]->native_handle());
}
return PosixConditionBase::WaitMultiple(std::move(handles), wait_all,
timeout);
if (is_alertable) alertable_state_ = true;
auto result =
PosixConditionBase::WaitMultiple(std::move(handles), wait_all, timeout);
if (is_alertable) alertable_state_ = false;
return result;
}
class PosixEvent : public PosixConditionHandle<Event> {
@@ -896,6 +925,7 @@ void* PosixCondition<Thread>::ThreadStartRoutine(void* parameter) {
std::unique_ptr<Thread> Thread::Create(CreationParameters params,
std::function<void()> start_routine) {
install_signal_handler(SignalType::kThreadSuspend);
install_signal_handler(SignalType::kThreadUserCallback);
auto thread = std::make_unique<PosixThread>();
if (!thread->Initialize(params, std::move(start_routine))) return nullptr;
assert_not_null(thread);
@@ -947,6 +977,14 @@ static void signal_handler(int signal, siginfo_t* info, void* /*context*/) {
assert_not_null(current_thread_);
current_thread_->WaitSuspended();
} break;
case SignalType::kThreadUserCallback: {
assert_not_null(info->si_value.sival_ptr);
auto p_thread =
static_cast<PosixCondition<Thread>*>(info->si_value.sival_ptr);
if (alertable_state_) {
p_thread->CallUserCallback();
}
} break;
default:
assert_always();
}