101 lines
3.3 KiB
C++
101 lines
3.3 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2022 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/chrono.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, kObjectType) {}
|
|
|
|
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;
|
|
}
|
|
assert_not_null(timer_);
|
|
}
|
|
|
|
X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
|
|
uint32_t routine, uint32_t routine_arg, bool resume) {
|
|
using xe::chrono::WinSystemClock;
|
|
using xe::chrono::XSystemClock;
|
|
// Caller is checking for STATUS_TIMER_RESUME_IGNORED.
|
|
if (resume) {
|
|
return X_STATUS_TIMER_RESUME_IGNORED;
|
|
}
|
|
|
|
period_ms = Clock::ScaleGuestDurationMillis(period_ms);
|
|
WinSystemClock::time_point due_tp;
|
|
if (due_time < 0) {
|
|
// Any timer implementation uses absolute times eventually, convert as early
|
|
// as possible for increased accuracy
|
|
auto after = xe::chrono::hundrednanoseconds(-due_time);
|
|
due_tp = date::clock_cast<WinSystemClock>(XSystemClock::now() + after);
|
|
} else {
|
|
due_tp = date::clock_cast<WinSystemClock>(
|
|
XSystemClock::from_file_time(due_time));
|
|
}
|
|
|
|
// 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<void()> 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<uint32_t>(time);
|
|
uint32_t time_high = static_cast<uint32_t>(time >> 32);
|
|
XELOGI(
|
|
"XTimer enqueuing timer callback to {:08X}({:08X}, {:08X}, {:08X})",
|
|
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_->SetOnceAt(due_tp, std::move(callback));
|
|
} else {
|
|
result = timer_->SetRepeatingAt(
|
|
due_tp, 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
|