From 9b4168cce947594024168fae5e71fde4358de114 Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 5 Mar 2022 11:46:34 +0100 Subject: [PATCH] [Base] Make HighResolutionTimer platform agnostic --- src/xenia/base/threading.h | 31 ++++++++++++--- src/xenia/base/threading_posix.cc | 65 ------------------------------- src/xenia/base/threading_win.cc | 43 +------------------- 3 files changed, 27 insertions(+), 112 deletions(-) diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index 63a63b4d4..08c34cd7d 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -27,6 +27,7 @@ #include "xenia/base/assert.h" #include "xenia/base/literals.h" #include "xenia/base/platform.h" +#include "xenia/base/threading_timer_queue.h" namespace xe { namespace threading { @@ -141,18 +142,38 @@ bool FreeTlsHandle(TlsHandle handle); uintptr_t GetTlsValue(TlsHandle handle); bool SetTlsValue(TlsHandle handle, uintptr_t value); -// A high-resolution timer capable of firing at millisecond-precision. -// All timers created in this way are executed in the same thread so -// callbacks must be kept short or else all timers will be impacted. +// A high-resolution timer capable of firing at millisecond-precision. All +// timers created in this way are executed in the same thread so callbacks must +// be kept short or else all timers will be impacted. This is a simplified +// wrapper around QueueTimerRecurring which automatically cancels the timer on +// destruction. class HighResolutionTimer { + HighResolutionTimer(std::chrono::milliseconds interval, + std::function callback) { + assert_not_null(callback); + wait_item_ = QueueTimerRecurring( + [callback = std::move(callback)](void*) { callback(); }, nullptr, + TimerQueueWaitItem::clock::now(), interval); + } + public: - virtual ~HighResolutionTimer() = default; + ~HighResolutionTimer() { + if (auto wait_item = wait_item_.lock()) { + wait_item->Disarm(); + } + } // Creates a new repeating timer with the given period. // The given function will be called back as close to the given period as // possible. static std::unique_ptr CreateRepeating( - std::chrono::milliseconds period, std::function callback); + std::chrono::milliseconds period, std::function callback) { + return std::unique_ptr( + new HighResolutionTimer(period, std::move(callback))); + } + + private: + std::weak_ptr wait_item_; }; // Results for a WaitHandle operation. diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index f0c315573..e341eb9d8 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -232,71 +232,6 @@ bool SetTlsValue(TlsHandle handle, uintptr_t value) { reinterpret_cast(value)) == 0; } -class PosixHighResolutionTimer : public HighResolutionTimer { - public: - explicit PosixHighResolutionTimer(std::function callback) - : valid_(false) { - callback_info_ = new timer_callback_info_t(std::move(callback)); - } - ~PosixHighResolutionTimer() override { - if (valid_) { - callback_info_->disarmed = true; - timer_delete(timerid_); - // Deliberately leaks memory when wait queue is full instead of blogs, - // check logs - static_cast(timers_garbage_collector_.TryScheduleAfter( - callback_info_, timers_garbage_collector_delay)); - } else { - delete callback_info_; - } - } - - bool Initialize(std::chrono::milliseconds period) { - if (valid_) { - // Double initialization - assert_always(); - return false; - } - // Create timer - sigevent sev{}; -#if XE_HAS_SIGEV_THREAD_ID - sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID; - sev.sigev_notify_thread_id = gettid(); -#else - sev.sigev_notify = SIGEV_SIGNAL; - callback_info_->target_thread = pthread_self(); -#endif - sev.sigev_signo = GetSystemSignal(SignalType::kHighResolutionTimer); - sev.sigev_value.sival_ptr = callback_info_; - if (timer_create(CLOCK_MONOTONIC, &sev, &timerid_) == -1) return false; - - // Start timer - itimerspec its{}; - its.it_value = DurationToTimeSpec(period); - its.it_interval = its.it_value; - valid_ = timer_settime(timerid_, 0, &its, nullptr) != -1; - if (!valid_) { - timer_delete(timerid_); - } - return valid_; - } - - private: - timer_callback_info_t* callback_info_; - timer_t timerid_; - bool valid_; // all values for timer_t are legal so we need this -}; - -std::unique_ptr HighResolutionTimer::CreateRepeating( - std::chrono::milliseconds period, std::function callback) { - install_signal_handler(SignalType::kHighResolutionTimer); - auto timer = std::make_unique(std::move(callback)); - if (!timer->Initialize(period)) { - return nullptr; - } - return std::move(timer); -} - class PosixConditionBase { public: virtual bool Signal() = 0; diff --git a/src/xenia/base/threading_win.cc b/src/xenia/base/threading_win.cc index cf760d603..60a3f7843 100644 --- a/src/xenia/base/threading_win.cc +++ b/src/xenia/base/threading_win.cc @@ -11,6 +11,7 @@ #include "xenia/base/logging.h" #include "xenia/base/platform_win.h" #include "xenia/base/threading.h" +#include "xenia/base/threading_timer_queue.h" #define LOG_LASTERROR() \ { XELOGI("Win32 Error 0x{:08X} in " __FUNCTION__ "(...)", GetLastError()); } @@ -112,48 +113,6 @@ bool SetTlsValue(TlsHandle handle, uintptr_t value) { return TlsSetValue(handle, reinterpret_cast(value)) ? true : false; } -class Win32HighResolutionTimer : public HighResolutionTimer { - public: - Win32HighResolutionTimer(std::function callback) - : callback_(std::move(callback)) {} - ~Win32HighResolutionTimer() override { - if (valid_) { - DeleteTimerQueueTimer(nullptr, handle_, INVALID_HANDLE_VALUE); - handle_ = nullptr; - } - } - - bool Initialize(std::chrono::milliseconds period) { - if (valid_) { - // Double initialization - assert_always(); - return false; - } - valid_ = !!CreateTimerQueueTimer( - &handle_, nullptr, - [](PVOID param, BOOLEAN timer_or_wait_fired) { - auto timer = reinterpret_cast(param); - timer->callback_(); - }, - this, 0, DWORD(period.count()), WT_EXECUTEINTIMERTHREAD); - return valid_; - } - - private: - std::function callback_; - HANDLE handle_ = nullptr; - bool valid_ = false; // Documentation does not state which HANDLE is invalid -}; - -std::unique_ptr HighResolutionTimer::CreateRepeating( - std::chrono::milliseconds period, std::function callback) { - auto timer = std::make_unique(std::move(callback)); - if (!timer->Initialize(period)) { - return nullptr; - } - return std::move(timer); -} - template class Win32Handle : public T { public: