diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index a63528460..ce136bf75 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -540,16 +540,9 @@ void KernelState::CompleteOverlappedEx(uint32_t overlapped_ptr, X_RESULT result, X_HANDLE thread_handle = XOverlappedGetContext(ptr); auto thread = object_table()->LookupObject(thread_handle); if (thread) { + // Queue APC on the thread that requested the overlapped operation. uint32_t routine = XOverlappedGetCompletionRoutine(ptr); - uint64_t args[] = { - result, length, overlapped_ptr, - }; - // TODO(benvanik): queue APC on the thread that requested the overlapped - // operation. - assert_always(); - // THIS IS WRONG, for testing only: - processor()->Execute(XThread::GetCurrentThread()->thread_state(), routine, - args, xe::countof(args)); + thread->EnqueueApc(routine, result, length, overlapped_ptr); } } } diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 72dfcfadd..01f6825d1 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -417,6 +417,9 @@ void XThread::Execute() { int exit_code = 0; + // Dispatch any APCs that were queued before the thread was created first. + DeliverAPCs(); + // If a XapiThreadStartup value is present, we use that as a trampoline. // Otherwise, we are a raw thread. if (creation_params_.xapi_thread_startup) { diff --git a/src/xenia/kernel/objects/xtimer.cc b/src/xenia/kernel/objects/xtimer.cc index 84e59a131..9256be02a 100644 --- a/src/xenia/kernel/objects/xtimer.cc +++ b/src/xenia/kernel/objects/xtimer.cc @@ -12,6 +12,7 @@ #include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/cpu/processor.h" +#include "xenia/kernel/objects/xthread.h" namespace xe { namespace kernel { @@ -42,25 +43,38 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms, return X_STATUS_TIMER_RESUME_IGNORED; } - // Stash routine for callback. - current_routine_ = routine; - current_routine_arg_ = routine_arg; - if (current_routine_) { - // Queue APC to call back routine with (arg, low, high). - // TODO(benvanik): APC dispatch. - XELOGE("Timer needs APC!"); - assert_zero(current_routine_); - } - due_time = Clock::ScaleGuestDurationFileTime(due_time); period_ms = Clock::ScaleGuestDurationMillis(period_ms); + // Stash routine for callback. + callback_thread_ = XThread::GetCurrentThread(); + callback_routine_ = routine; + callback_routine_arg_ = routine_arg; + + // This callback will only be issued when the timer is fired. + std::function callback = nullptr; + if (callback_routine_) { + callback = [this]() { + // Queue APC to call back routine with (arg, low, high). + // It'll be executed on the thread that requested the timer. + uint64_t time = xe::Clock::QueryGuestSystemTime(); + uint32_t time_low = static_cast(time); + uint32_t time_high = static_cast(time >> 32); + XELOGI("XTimer enqueuing timer callback to %.8X(%.8X, %.8X, %.8X)", + callback_routine_, callback_routine_arg_, time_low, time_high); + callback_thread_->EnqueueApc(callback_routine_, callback_routine_arg_, + time_low, time_high); + }; + } + bool result; if (!period_ms) { - result = timer_->SetOnce(std::chrono::nanoseconds(due_time * 100)); + result = timer_->SetOnce(std::chrono::nanoseconds(due_time * 100), + std::move(callback)); } else { result = timer_->SetRepeating(std::chrono::nanoseconds(due_time * 100), - std::chrono::milliseconds(period_ms)); + std::chrono::milliseconds(period_ms), + std::move(callback)); } return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; diff --git a/src/xenia/kernel/objects/xtimer.h b/src/xenia/kernel/objects/xtimer.h index 1bb40aa96..9c81f8f05 100644 --- a/src/xenia/kernel/objects/xtimer.h +++ b/src/xenia/kernel/objects/xtimer.h @@ -17,6 +17,8 @@ namespace xe { namespace kernel { +class XThread; + class XTimer : public XObject { public: explicit XTimer(KernelState* kernel_state); @@ -24,7 +26,6 @@ class XTimer : public XObject { void Initialize(uint32_t timer_type); - // completion routine, arg to completion routine X_STATUS SetTimer(int64_t due_time, uint32_t period_ms, uint32_t routine, uint32_t routine_arg, bool resume); X_STATUS Cancel(); @@ -34,8 +35,9 @@ class XTimer : public XObject { private: std::unique_ptr timer_; - uint32_t current_routine_; - uint32_t current_routine_arg_; + XThread* callback_thread_ = nullptr; + uint32_t callback_routine_ = 0; + uint32_t callback_routine_arg_ = 0; }; } // namespace kernel