diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index 417b05fee..0e1aab4e9 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -67,6 +67,123 @@ void Sleep(std::chrono::duration duration) { Sleep(std::chrono::duration_cast(duration)); } +class WaitHandle { + public: + // bool Wait(); + // static bool Wait(); + // static bool SignalAndWait(); + // static bool WaitMultiple(); + + protected: + WaitHandle() = default; +}; + +// Models a Win32-like event object. +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx +class Event : public WaitHandle { + public: + // Creates a manual-reset event object, which requires the use of the + // Reset() function to set the event state to nonsignaled. + // If initial_state is true the event will start in the signaled state. + static std::unique_ptr CreateManualResetEvent(bool initial_state); + + // Creates an auto-reset event object, and system automatically resets the + // event state to nonsignaled after a single waiting thread has been released. + // If initial_state is true the event will start in the signaled state. + static std::unique_ptr CreateAutoResetEvent(bool initial_state); + + // Sets the specified event object to the signaled state. + // If this is a manual reset event the event stays signaled until Reset() is + // called. If this is an auto reset event until exactly one wait is satisfied. + virtual void Set() = 0; + + // Sets the specified event object to the nonsignaled state. + // Resetting an event that is already reset has no effect. + virtual void Reset() = 0; + + // Sets the specified event object to the signaled state and then resets it to + // the nonsignaled state after releasing the appropriate number of waiting + // threads. + virtual void Pulse() = 0; +}; + +// Models a Win32-like semaphore object. +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682438(v=vs.85).aspx +class Semaphore : public WaitHandle { + public: + // Creates a new semaphore object. + // The initial_count must be greater than or equal to zero and less than or + // equal to maximum_count. The state of a semaphore is signaled when its count + // is greater than zero and nonsignaled when it is zero. The count is + // decreased by one whenever a wait function releases a thread that was + // waiting for the semaphore. The count is increased by a specified amount by + // calling the Release() function. + std::unique_ptr Create(int initial_count, int maximum_count); + + // Increases the count of the specified semaphore object by a specified + // amount. + // release_count must be greater than zero. + // Returns false if adding release_count would set the semaphore over the + // initially specified maximum_count. + virtual bool Release(int release_count, int* out_previous_count) = 0; +}; + +// Models a Win32-like mutant (mutex) object. +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx +class Mutant : public WaitHandle { + public: + // Creates a new mutant object, initially owned by the calling thread if + // specified. + std::unique_ptr Create(bool initial_owner); + + // Releases ownership of the specified mutex object. + // Returns false if the calling thread does not own the mutant object. + virtual bool Release() = 0; +}; + +class Timer : public WaitHandle { + public: + // Creates a timer whose state remains signaled until SetOnce() or + // SetRepeating() is called to establish a new due time. + std::unique_ptr CreateManualResetTimer(); + + // Creates a timer whose state remains signaled until a thread completes a + // wait operation on the timer object. + std::unique_ptr CreateSynchronizationTimer(); + + // Activates the specified waitable timer. When the due time arrives, the + // timer is signaled and the thread that set the timer calls the optional + // completion routine. + // Returns true on success. + virtual bool SetOnce(std::chrono::nanoseconds due_time, + std::function opt_callback = nullptr) = 0; + + // Activates the specified waitable timer. When the due time arrives, the + // timer is signaled and the thread that set the timer calls the optional + // completion routine. A periodic timer automatically reactivates each time + // the period elapses, until the timer is canceled or reset. + // Returns true on success. + virtual bool SetRepeating(std::chrono::nanoseconds due_time, + std::chrono::milliseconds period, + std::function opt_callback = nullptr) = 0; + template + void SetRepeating(std::chrono::nanoseconds due_time, + std::chrono::duration period, + std::function opt_callback = nullptr) { + SetRepeating(due_time, + std::chrono::duration_cast(period), + std::move(opt_callback)); + } + + // Stops the timer before it can be set to the signaled state and cancels + // outstanding callbacks. Threads performing a wait operation on the timer + // remain waiting until they time out or the timer is reactivated and its + // state is set to signaled. If the timer is already in the signaled state, it + // remains in that state. + // Returns true on success. + virtual bool Cancel() = 0; +}; + } // namespace threading } // namespace xe diff --git a/src/xenia/base/threading_win.cc b/src/xenia/base/threading_win.cc index 61315d6bf..ed65a5c8d 100644 --- a/src/xenia/base/threading_win.cc +++ b/src/xenia/base/threading_win.cc @@ -67,5 +67,130 @@ void Sleep(std::chrono::microseconds duration) { } } +class Win32Handle { + public: + Win32Handle(HANDLE handle) : handle_(handle) {} + virtual ~Win32Handle() { + CloseHandle(handle_); + handle_ = nullptr; + } + + protected: + HANDLE handle_ = nullptr; +}; + +class Win32Event : public Event, public Win32Handle { + public: + Win32Event(HANDLE handle) : Win32Handle(handle) {} + ~Win32Event() = default; + void Set() override { SetEvent(handle_); } + void Reset() override { ResetEvent(handle_); } + void Pulse() override { PulseEvent(handle_); } +}; + +std::unique_ptr Event::CreateManualResetEvent(bool initial_state) { + return std::make_unique( + CreateEvent(nullptr, TRUE, initial_state ? TRUE : FALSE, nullptr)); +} + +std::unique_ptr Event::CreateAutoResetEvent(bool initial_state) { + return std::make_unique( + CreateEvent(nullptr, FALSE, initial_state ? TRUE : FALSE, nullptr)); +} + +class Win32Semaphore : public Semaphore, public Win32Handle { + public: + Win32Semaphore(HANDLE handle) : Win32Handle(handle) {} + ~Win32Semaphore() override = default; + bool Release(int release_count, int* out_previous_count) override { + return ReleaseSemaphore(handle_, release_count, + reinterpret_cast(out_previous_count)) + ? true + : false; + } +}; + +std::unique_ptr Semaphore::Create(int initial_count, + int maximum_count) { + return std::make_unique( + CreateSemaphore(nullptr, initial_count, maximum_count, nullptr)); +} + +class Win32Mutant : public Mutant, public Win32Handle { + public: + Win32Mutant(HANDLE handle) : Win32Handle(handle) {} + ~Win32Mutant() = default; + bool Release() override { return ReleaseMutex(handle_) ? true : false; } +}; + +std::unique_ptr Mutant::Create(bool initial_owner) { + return std::make_unique( + CreateMutex(nullptr, initial_owner ? TRUE : FALSE, nullptr)); +} + +class Win32Timer : public Timer, public Win32Handle { + public: + Win32Timer(HANDLE handle) : Win32Handle(handle) {} + ~Win32Timer() = default; + bool SetOnce(std::chrono::nanoseconds due_time, + std::function opt_callback) override { + std::lock_guard lock(mutex_); + callback_ = std::move(opt_callback); + LARGE_INTEGER due_time_li; + due_time_li.QuadPart = due_time.count() / 100; + auto completion_routine = + opt_callback ? reinterpret_cast(CompletionRoutine) + : NULL; + return SetWaitableTimer(handle_, &due_time_li, 0, completion_routine, this, + FALSE) + ? true + : false; + } + bool SetRepeating(std::chrono::nanoseconds due_time, + std::chrono::milliseconds period, + std::function opt_callback) override { + std::lock_guard lock(mutex_); + callback_ = std::move(opt_callback); + LARGE_INTEGER due_time_li; + due_time_li.QuadPart = due_time.count() / 100; + auto completion_routine = + opt_callback ? reinterpret_cast(CompletionRoutine) + : NULL; + return SetWaitableTimer(handle_, &due_time_li, int32_t(period.count()), + completion_routine, this, FALSE) + ? true + : false; + } + bool Cancel() override { + // Reset the callback immediately so that any completions don't call it. + std::lock_guard lock(mutex_); + callback_ = nullptr; + return CancelWaitableTimer(handle_) ? true : false; + } + + private: + static void CompletionRoutine(Win32Timer* timer, DWORD timer_low, + DWORD timer_high) { + // As the callback may reset the timer, store local. + std::function callback; + { + std::lock_guard lock(timer->mutex_); + callback = timer->callback_; + } + callback(); + } + + std::mutex mutex_; + std::function callback_; +}; + +std::unique_ptr Timer::CreateManualResetTimer() { + return std::make_unique(CreateWaitableTimer(NULL, TRUE, NULL)); +} + +std::unique_ptr Timer::CreateSynchronizationTimer() { + return std::make_unique(CreateWaitableTimer(NULL, FALSE, NULL)); +} + } // namespace threading } // namespace xe diff --git a/src/xenia/kernel/objects/xtimer.cc b/src/xenia/kernel/objects/xtimer.cc index 8052775b6..b8abe6748 100644 --- a/src/xenia/kernel/objects/xtimer.cc +++ b/src/xenia/kernel/objects/xtimer.cc @@ -46,6 +46,11 @@ void XTimer::Initialize(uint32_t timer_type) { 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; + } + // Stash routine for callback. current_routine_ = routine; current_routine_arg_ = routine_arg; @@ -60,12 +65,6 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms, routine ? (PTIMERAPCROUTINE)CompletionRoutine : NULL, this, resume ? TRUE : FALSE); - // Caller is checking for STATUS_TIMER_RESUME_IGNORED. - // This occurs if result == TRUE but error is set. - if (!result && GetLastError() == ERROR_NOT_SUPPORTED) { - return X_STATUS_TIMER_RESUME_IGNORED; - } - return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; }