/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2013 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/kernel/xtimer.h" #include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/cpu/processor.h" #include "xenia/kernel/xthread.h" namespace xe { namespace kernel { XTimer::XTimer(KernelState* kernel_state) : XObject(kernel_state, kType) {} XTimer::~XTimer() = default; void XTimer::Initialize(uint32_t timer_type) { assert_false(timer_); switch (timer_type) { case 0: // NotificationTimer timer_ = xe::threading::Timer::CreateManualResetTimer(); break; case 1: // SynchronizationTimer timer_ = xe::threading::Timer::CreateSynchronizationTimer(); break; default: assert_always(); break; } } X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms, uint32_t routine, uint32_t routine_arg, bool resume) { // Caller is checking for STATUS_TIMER_RESUME_IGNORED. if (resume) { return X_STATUS_TIMER_RESUME_IGNORED; } 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), std::move(callback)); } else { result = timer_->SetRepeating(std::chrono::nanoseconds(due_time * 100), std::chrono::milliseconds(period_ms), std::move(callback)); } return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; } X_STATUS XTimer::Cancel() { return timer_->Cancel() ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; } } // namespace kernel } // namespace xe