From 345fe60da076bed9136bcd626fe1e770cbcc9ef9 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Tue, 14 Jul 2015 22:44:45 -0700 Subject: [PATCH] Switching over kernel objects to the platform-agnostic APIs. Possibly some regressions here. --- src/xenia/base/threading.h | 97 ++++++- src/xenia/base/threading_win.cc | 131 +++++++++ src/xenia/kernel/objects/xevent.cc | 39 +-- src/xenia/kernel/objects/xevent.h | 8 +- src/xenia/kernel/objects/xfile.cc | 6 +- src/xenia/kernel/objects/xfile.h | 16 +- src/xenia/kernel/objects/xmutant.cc | 17 +- src/xenia/kernel/objects/xmutant.h | 8 +- src/xenia/kernel/objects/xnotify_listener.cc | 19 +- src/xenia/kernel/objects/xnotify_listener.h | 13 +- src/xenia/kernel/objects/xsemaphore.cc | 18 +- src/xenia/kernel/objects/xsemaphore.h | 10 +- src/xenia/kernel/objects/xthread.cc | 287 ++++++------------- src/xenia/kernel/objects/xthread.h | 12 +- src/xenia/kernel/objects/xtimer.cc | 50 ++-- src/xenia/kernel/objects/xtimer.h | 11 +- src/xenia/kernel/xobject.cc | 102 +++++-- src/xenia/kernel/xobject.h | 3 +- 18 files changed, 491 insertions(+), 356 deletions(-) diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index df6c64c4c..5c876a028 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -45,7 +45,8 @@ class Fence { std::atomic signaled_; }; -// TODO(benvanik): processor info API. +// Returns the total number of logical processors in the host system. +uint32_t logical_processor_count(); // Gets a stable thread-specific ID, but may not be. Use for informative // purposes only. @@ -69,6 +70,21 @@ void Sleep(std::chrono::duration duration) { Sleep(std::chrono::duration_cast(duration)); } +enum class SleepResult { + kSuccess, + kAlerted, +}; +// Sleeps the current thread for at least as long as the given duration. +// The thread is put in an alertable state and may wake to dispatch user +// callbacks. If this happens the sleep returns early with +// SleepResult::kAlerted. +SleepResult AlertableSleep(std::chrono::microseconds duration); +template +SleepResult AlertableSleep(std::chrono::duration duration) { + return AlertableSleep( + std::chrono::duration_cast(duration)); +} + // Results for a WaitHandle operation. enum class WaitResult { // The state of the specified object is signaled. @@ -236,6 +252,8 @@ class Mutant : public WaitHandle { virtual bool Release() = 0; }; +// Models a Win32-like timer object. +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms687012(v=vs.85).aspx class Timer : public WaitHandle { public: // Creates a timer whose state remains signaled until SetOnce() or @@ -279,6 +297,83 @@ class Timer : public WaitHandle { virtual bool Cancel() = 0; }; +struct ThreadPriority { + static const int32_t kLowest = -2; + static const int32_t kBelowNormal = -1; + static const int32_t kNormal = 0; + static const int32_t kAboveNormal = 1; + static const int32_t kHighest = 2; +}; + +// Models a Win32-like thread object. +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx +class Thread : public WaitHandle { + public: + struct CreationParameters { + size_t stack_size = 4 * 1024 * 1024; + bool create_suspended = false; + int32_t initial_priority = 0; + }; + + // Creates a thread with the given parameters and calls the start routine from + // within that thread. + static std::unique_ptr Create(CreationParameters params, + std::function start_routine); + + // Returns the current name of the thread, if previously specified. + std::string name() const { return name_; } + + // Sets the name of the thread, used in debugging and logging. + virtual void set_name(std::string name) { name_ = std::move(name); } + + // Returns the current priority value for the thread. + virtual int32_t priority() = 0; + + // Sets the priority value for the thread. This value, together with the + // priority class of the thread's process, determines the thread's base + // priority level. ThreadPriority contains useful constants. + virtual void set_priority(int32_t new_priority) = 0; + + // Returns the current processor affinity mask for the thread. + virtual uint64_t affinity_mask() = 0; + + // Sets a processor affinity mask for the thread. + // A thread affinity mask is a bit vector in which each bit represents a + // logical processor that a thread is allowed to run on. A thread affinity + // mask must be a subset of the process affinity mask for the containing + // process of a thread. + virtual void set_affinity_mask(uint64_t new_affinity_mask) = 0; + + // Adds a user-mode asynchronous procedure call request to the thread queue. + // When a user-mode APC is queued, the thread is not directed to call the APC + // function unless it is in an alertable state. After the thread is in an + // alertable state, the thread handles all pending APCs in first in, first out + // (FIFO) order, and the wait operation returns WaitResult::kUserCallback. + virtual void QueueUserCallback(std::function callback) = 0; + + // Decrements a thread's suspend count. When the suspend count is decremented + // to zero, the execution of the thread is resumed. + virtual bool Resume(uint32_t* out_new_suspend_count = nullptr) = 0; + + // Suspends the specified thread. + virtual bool Suspend(uint32_t* out_previous_suspend_count = nullptr) = 0; + + // Ends the calling thread. + // No destructors are called, and this function does not return. + // The state of the thread object becomes signaled, releasing any other + // threads that had been waiting for the thread to terminate. + virtual void Exit(int exit_code) = 0; + + // Terminates the thread. + // No destructors are called, and this function does not return. + // The state of the thread object becomes signaled, releasing any other + // threads that had been waiting for the thread to terminate. + virtual void Terminate(int exit_code) = 0; + + protected: + std::string name_; +}; + } // namespace threading } // namespace xe diff --git a/src/xenia/base/threading_win.cc b/src/xenia/base/threading_win.cc index dcf0ad9f8..609cb348e 100644 --- a/src/xenia/base/threading_win.cc +++ b/src/xenia/base/threading_win.cc @@ -9,11 +9,23 @@ #include "xenia/base/threading.h" +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" #include "xenia/base/platform_win.h" namespace xe { namespace threading { +uint32_t logical_processor_count() { + static uint32_t value = 0; + if (!value) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + value = system_info.dwNumberOfProcessors; + } + return value; +} + uint32_t current_thread_id() { return static_cast(GetCurrentThreadId()); } @@ -67,6 +79,14 @@ void Sleep(std::chrono::microseconds duration) { } } +SleepResult AlertableSleep(std::chrono::microseconds duration) { + if (SleepEx(static_cast(duration.count() / 1000), TRUE) == + WAIT_IO_COMPLETION) { + return SleepResult::kAlerted; + } + return SleepResult::kSuccess; +} + template class Win32Handle : public T { public: @@ -268,5 +288,116 @@ std::unique_ptr Timer::CreateSynchronizationTimer() { return std::make_unique(CreateWaitableTimer(NULL, FALSE, NULL)); } +class Win32Thread : public Win32Handle { + public: + Win32Thread(HANDLE handle) : Win32Handle(handle) {} + ~Win32Thread() = default; + + void set_name(std::string name) override { + AssertCallingThread(); + xe::threading::set_name(name); + Thread::set_name(name); + } + + int32_t priority() override { return GetThreadPriority(handle_); } + + void set_priority(int32_t new_priority) override { + SetThreadPriority(handle_, new_priority); + } + + uint64_t affinity_mask() override { + uint64_t value = 0; + SetThreadAffinityMask(handle_, reinterpret_cast(&value)); + return value; + } + + void set_affinity_mask(uint64_t new_affinity_mask) override { + SetThreadAffinityMask(handle_, new_affinity_mask); + } + + struct ApcData { + std::function callback; + }; + static void NTAPI DispatchApc(ULONG_PTR parameter) { + auto apc_data = reinterpret_cast(parameter); + apc_data->callback(); + delete apc_data; + } + + void QueueUserCallback(std::function callback) override { + auto apc_data = new ApcData({std::move(callback)}); + QueueUserAPC(DispatchApc, handle_, reinterpret_cast(apc_data)); + } + + bool Resume(uint32_t* out_new_suspend_count = nullptr) override { + if (out_new_suspend_count) { + *out_new_suspend_count = 0; + } + DWORD result = ResumeThread(handle_); + if (result == UINT_MAX) { + return false; + } + if (out_new_suspend_count) { + *out_new_suspend_count = result; + } + return true; + } + + bool Suspend(uint32_t* out_previous_suspend_count = nullptr) override { + if (out_previous_suspend_count) { + *out_previous_suspend_count = 0; + } + DWORD result = SuspendThread(handle_); + if (result == UINT_MAX) { + return false; + } + if (out_previous_suspend_count) { + *out_previous_suspend_count = result; + } + return true; + } + + void Exit(int exit_code) override { + AssertCallingThread(); + ExitThread(exit_code); + } + + void Terminate(int exit_code) override { + TerminateThread(handle_, exit_code); + } + + private: + void AssertCallingThread() { + assert_true(GetCurrentThreadId() == GetThreadId(handle_)); + } +}; + +struct ThreadStartData { + std::function start_routine; +}; +DWORD WINAPI ThreadStartRoutine(LPVOID parameter) { + auto start_data = reinterpret_cast(parameter); + start_data->start_routine(); + delete start_data; + return 0; +} + +std::unique_ptr Thread::Create(CreationParameters params, + std::function start_routine) { + auto start_data = new ThreadStartData({std::move(start_routine)}); + HANDLE handle = + CreateThread(NULL, params.stack_size, ThreadStartRoutine, start_data, + params.create_suspended ? CREATE_SUSPENDED : 0, NULL); + if (!handle) { + // TODO(benvanik): pass back? + auto last_error = GetLastError(); + XELOGE("Unable to CreateThread: %d", last_error); + delete start_data; + return nullptr; + } + GetThreadId(handle); + return std::make_unique(handle); +} + } // namespace threading } // namespace xe diff --git a/src/xenia/kernel/objects/xevent.cc b/src/xenia/kernel/objects/xevent.cc index 8ef30bfd1..f5de20e23 100644 --- a/src/xenia/kernel/objects/xevent.cc +++ b/src/xenia/kernel/objects/xevent.cc @@ -13,23 +13,22 @@ namespace xe { namespace kernel { -XEvent::XEvent(KernelState* kernel_state) - : XObject(kernel_state, kTypeEvent), native_handle_(NULL) {} +XEvent::XEvent(KernelState* kernel_state) : XObject(kernel_state, kTypeEvent) {} -XEvent::~XEvent() { - if (native_handle_) { - CloseHandle(native_handle_); +XEvent::~XEvent() = default; + +void XEvent::Initialize(bool manual_reset, bool initial_state) { + assert_false(event_); + + if (manual_reset) { + event_ = xe::threading::Event::CreateManualResetEvent(initial_state); + } else { + event_ = xe::threading::Event::CreateAutoResetEvent(initial_state); } } -void XEvent::Initialize(bool manual_reset, bool initial_state) { - assert_null(native_handle_); - - native_handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL); -} - void XEvent::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { - assert_null(native_handle_); + assert_false(event_); bool manual_reset; switch (header.type) { @@ -45,21 +44,25 @@ void XEvent::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { } bool initial_state = header.signal_state ? true : false; - - native_handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL); + Initialize(manual_reset, initial_state); } int32_t XEvent::Set(uint32_t priority_increment, bool wait) { - return SetEvent(native_handle_) ? 1 : 0; + event_->Set(); + return 1; } int32_t XEvent::Pulse(uint32_t priority_increment, bool wait) { - return PulseEvent(native_handle_) ? 1 : 0; + event_->Pulse(); + return 1; } -int32_t XEvent::Reset() { return ResetEvent(native_handle_) ? 1 : 0; } +int32_t XEvent::Reset() { + event_->Reset(); + return 1; +} -void XEvent::Clear() { ResetEvent(native_handle_); } +void XEvent::Clear() { event_->Reset(); } } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/objects/xevent.h b/src/xenia/kernel/objects/xevent.h index 2071851d2..31cbb9df6 100644 --- a/src/xenia/kernel/objects/xevent.h +++ b/src/xenia/kernel/objects/xevent.h @@ -10,7 +10,7 @@ #ifndef XENIA_KERNEL_XBOXKRNL_XEVENT_H_ #define XENIA_KERNEL_XBOXKRNL_XEVENT_H_ -#include "xenia/base/platform_win.h" +#include "xenia/base/threading.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -20,7 +20,7 @@ namespace kernel { class XEvent : public XObject { public: XEvent(KernelState* kernel_state); - virtual ~XEvent(); + ~XEvent() override; void Initialize(bool manual_reset, bool initial_state); void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header); @@ -30,10 +30,10 @@ class XEvent : public XObject { int32_t Reset(); void Clear(); - virtual void* GetWaitHandle() { return native_handle_; } + xe::threading::WaitHandle* GetWaitHandle() override { return event_.get(); } private: - HANDLE native_handle_; + std::unique_ptr event_; }; } // namespace kernel diff --git a/src/xenia/kernel/objects/xfile.cc b/src/xenia/kernel/objects/xfile.cc index 0094b18b3..898157121 100644 --- a/src/xenia/kernel/objects/xfile.cc +++ b/src/xenia/kernel/objects/xfile.cc @@ -19,9 +19,7 @@ namespace kernel { XFile::XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry) : XObject(kernel_state, kTypeFile), entry_(entry), - file_access_(file_access), - position_(0), - find_index_(0) { + file_access_(file_access) { async_event_ = new XEvent(kernel_state); async_event_->Initialize(false, false); } @@ -32,8 +30,6 @@ XFile::~XFile() { async_event_->Delete(); } -void* XFile::GetWaitHandle() { return async_event_->GetWaitHandle(); } - X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length, const char* file_name, bool restart) { diff --git a/src/xenia/kernel/objects/xfile.h b/src/xenia/kernel/objects/xfile.h index dd046c92c..8a1685d62 100644 --- a/src/xenia/kernel/objects/xfile.h +++ b/src/xenia/kernel/objects/xfile.h @@ -11,6 +11,7 @@ #define XENIA_KERNEL_XBOXKRNL_XFILE_H_ #include "xenia/base/filesystem.h" +#include "xenia/kernel/objects/xevent.h" #include "xenia/kernel/xobject.h" #include "xenia/vfs/device.h" #include "xenia/vfs/entry.h" @@ -21,7 +22,6 @@ namespace xe { namespace kernel { class XAsyncRequest; -class XEvent; // https://msdn.microsoft.com/en-us/library/windows/hardware/ff545822.aspx struct X_FILE_NETWORK_OPEN_INFORMATION { @@ -100,7 +100,9 @@ class XFile : public XObject { X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_written); - virtual void* GetWaitHandle(); + xe::threading::WaitHandle* GetWaitHandle() override { + return async_event_->GetWaitHandle(); + } protected: XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry); @@ -112,16 +114,16 @@ class XFile : public XObject { } private: - vfs::Entry* entry_; - uint32_t file_access_; - XEvent* async_event_; + vfs::Entry* entry_ = nullptr; + uint32_t file_access_ = 0; + XEvent* async_event_ = nullptr; // TODO(benvanik): create flags, open state, etc. - size_t position_; + size_t position_ = 0; xe::filesystem::WildcardEngine find_engine_; - size_t find_index_; + size_t find_index_ = 0; }; } // namespace kernel diff --git a/src/xenia/kernel/objects/xmutant.cc b/src/xenia/kernel/objects/xmutant.cc index a26e8c79f..6455b456c 100644 --- a/src/xenia/kernel/objects/xmutant.cc +++ b/src/xenia/kernel/objects/xmutant.cc @@ -13,22 +13,18 @@ namespace xe { namespace kernel { XMutant::XMutant(KernelState* kernel_state) - : XObject(kernel_state, kTypeMutant), native_handle_(NULL) {} + : XObject(kernel_state, kTypeMutant) {} -XMutant::~XMutant() { - if (native_handle_) { - CloseHandle(native_handle_); - } -} +XMutant::~XMutant() = default; void XMutant::Initialize(bool initial_owner) { - assert_null(native_handle_); + assert_false(mutant_); - native_handle_ = CreateMutex(NULL, initial_owner ? TRUE : FALSE, NULL); + mutant_ = xe::threading::Mutant::Create(initial_owner); } void XMutant::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { - assert_null(native_handle_); + assert_false(mutant_); // Haven't seen this yet, but it's possible. assert_always(); @@ -38,8 +34,7 @@ X_STATUS XMutant::ReleaseMutant(uint32_t priority_increment, bool abandon, bool wait) { // TODO(benvanik): abandoning. assert_false(abandon); - BOOL result = ReleaseMutex(native_handle_); - if (result) { + if (mutant_->Release()) { return X_STATUS_SUCCESS; } else { return X_STATUS_MUTANT_NOT_OWNED; diff --git a/src/xenia/kernel/objects/xmutant.h b/src/xenia/kernel/objects/xmutant.h index f72752b8d..6d8121350 100644 --- a/src/xenia/kernel/objects/xmutant.h +++ b/src/xenia/kernel/objects/xmutant.h @@ -10,7 +10,7 @@ #ifndef XENIA_KERNEL_XBOXKRNL_XMUTANT_H_ #define XENIA_KERNEL_XBOXKRNL_XMUTANT_H_ -#include "xenia/base/platform_win.h" +#include "xenia/base/threading.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -20,17 +20,17 @@ namespace kernel { class XMutant : public XObject { public: XMutant(KernelState* kernel_state); - virtual ~XMutant(); + ~XMutant() override; void Initialize(bool initial_owner); void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header); X_STATUS ReleaseMutant(uint32_t priority_increment, bool abandon, bool wait); - virtual void* GetWaitHandle() { return native_handle_; } + xe::threading::WaitHandle* GetWaitHandle() override { return mutant_.get(); } private: - HANDLE native_handle_; + std::unique_ptr mutant_; }; } // namespace kernel diff --git a/src/xenia/kernel/objects/xnotify_listener.cc b/src/xenia/kernel/objects/xnotify_listener.cc index 7700d6793..db93a327b 100644 --- a/src/xenia/kernel/objects/xnotify_listener.cc +++ b/src/xenia/kernel/objects/xnotify_listener.cc @@ -9,29 +9,22 @@ #include "xenia/kernel/objects/xnotify_listener.h" -#include "xenia/base/platform_win.h" #include "xenia/kernel/kernel_state.h" namespace xe { namespace kernel { XNotifyListener::XNotifyListener(KernelState* kernel_state) - : XObject(kernel_state, kTypeNotifyListener), - wait_handle_(NULL), - mask_(0), - notification_count_(0) {} + : XObject(kernel_state, kTypeNotifyListener) {} XNotifyListener::~XNotifyListener() { kernel_state_->UnregisterNotifyListener(this); - if (wait_handle_) { - CloseHandle(wait_handle_); - } } void XNotifyListener::Initialize(uint64_t mask) { - assert_null(wait_handle_); + assert_false(wait_handle_); - wait_handle_ = CreateEvent(NULL, TRUE, FALSE, NULL); + wait_handle_ = xe::threading::Event::CreateManualResetEvent(false); mask_ = mask; kernel_state_->RegisterNotifyListener(this); @@ -52,7 +45,7 @@ void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) { notification_count_++; notifications_.insert({id, data}); } - SetEvent(wait_handle_); + wait_handle_->Set(); } bool XNotifyListener::DequeueNotification(XNotificationID* out_id, @@ -67,7 +60,7 @@ bool XNotifyListener::DequeueNotification(XNotificationID* out_id, notifications_.erase(it); notification_count_--; if (!notification_count_) { - ResetEvent(wait_handle_); + wait_handle_->Reset(); } } return dequeued; @@ -85,7 +78,7 @@ bool XNotifyListener::DequeueNotification(XNotificationID id, notifications_.erase(it); notification_count_--; if (!notification_count_) { - ResetEvent(wait_handle_); + wait_handle_->Reset(); } } } diff --git a/src/xenia/kernel/objects/xnotify_listener.h b/src/xenia/kernel/objects/xnotify_listener.h index 1f18e0048..2516fff4c 100644 --- a/src/xenia/kernel/objects/xnotify_listener.h +++ b/src/xenia/kernel/objects/xnotify_listener.h @@ -10,10 +10,11 @@ #ifndef XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_ #define XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_ -#include +#include #include #include "xenia/base/mutex.h" +#include "xenia/base/threading.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -25,7 +26,7 @@ namespace kernel { class XNotifyListener : public XObject { public: XNotifyListener(KernelState* kernel_state); - virtual ~XNotifyListener(); + ~XNotifyListener() override; uint64_t mask() const { return mask_; } @@ -35,14 +36,14 @@ class XNotifyListener : public XObject { bool DequeueNotification(XNotificationID* out_id, uint32_t* out_data); bool DequeueNotification(XNotificationID id, uint32_t* out_data); - virtual void* GetWaitHandle() { return wait_handle_; } + xe::threading::WaitHandle* GetWaitHandle() { return wait_handle_.get(); } private: - HANDLE wait_handle_; + std::unique_ptr wait_handle_; xe::mutex lock_; std::unordered_map notifications_; - size_t notification_count_; - uint64_t mask_; + size_t notification_count_ = 0; + uint64_t mask_ = 0; }; } // namespace kernel diff --git a/src/xenia/kernel/objects/xsemaphore.cc b/src/xenia/kernel/objects/xsemaphore.cc index 4f39b09a7..6331a3480 100644 --- a/src/xenia/kernel/objects/xsemaphore.cc +++ b/src/xenia/kernel/objects/xsemaphore.cc @@ -13,32 +13,28 @@ namespace xe { namespace kernel { XSemaphore::XSemaphore(KernelState* kernel_state) - : XObject(kernel_state, kTypeSemaphore), native_handle_(NULL) {} + : XObject(kernel_state, kTypeSemaphore) {} -XSemaphore::~XSemaphore() { - if (native_handle_) { - CloseHandle(native_handle_); - } -} +XSemaphore::~XSemaphore() = default; void XSemaphore::Initialize(int32_t initial_count, int32_t maximum_count) { - assert_null(native_handle_); + assert_false(semaphore_); CreateNative(sizeof(X_SEMAPHORE)); - native_handle_ = CreateSemaphore(NULL, initial_count, maximum_count, NULL); + semaphore_ = xe::threading::Semaphore::Create(initial_count, maximum_count); } void XSemaphore::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) { - assert_null(native_handle_); + assert_false(semaphore_); // NOT IMPLEMENTED // We expect Initialize to be called shortly. } int32_t XSemaphore::ReleaseSemaphore(int32_t release_count) { - LONG previous_count = 0; - ::ReleaseSemaphore(native_handle_, release_count, &previous_count); + int32_t previous_count = 0; + semaphore_->Release(release_count, &previous_count); return previous_count; } diff --git a/src/xenia/kernel/objects/xsemaphore.h b/src/xenia/kernel/objects/xsemaphore.h index ae3eff029..73019b4c5 100644 --- a/src/xenia/kernel/objects/xsemaphore.h +++ b/src/xenia/kernel/objects/xsemaphore.h @@ -10,7 +10,7 @@ #ifndef XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_ #define XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_ -#include "xenia/base/platform_win.h" +#include "xenia/base/threading.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -25,17 +25,19 @@ struct X_SEMAPHORE { class XSemaphore : public XObject { public: XSemaphore(KernelState* kernel_state); - virtual ~XSemaphore(); + ~XSemaphore() override; void Initialize(int32_t initial_count, int32_t maximum_count); void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header); int32_t ReleaseSemaphore(int32_t release_count); - virtual void* GetWaitHandle() { return native_handle_; } + xe::threading::WaitHandle* GetWaitHandle() override { + return semaphore_.get(); + } private: - HANDLE native_handle_; + std::unique_ptr semaphore_; }; } // namespace kernel diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 538f82324..22cafcf2a 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -45,7 +45,6 @@ XThread::XThread(KernelState* kernel_state, uint32_t stack_size, bool guest_thread) : XObject(kernel_state, kTypeThread), thread_id_(++next_xthread_id), - thread_handle_(0), pcr_address_(0), thread_state_address_(0), thread_state_(0), @@ -84,7 +83,7 @@ XThread::~XThread() { delete apc_list_; - PlatformDestroy(); + thread_.reset(); if (thread_state_) { delete thread_state_; @@ -93,7 +92,7 @@ XThread::~XThread() { kernel_state()->memory()->SystemHeapFree(tls_address_); kernel_state()->memory()->SystemHeapFree(pcr_address_); - if (thread_handle_) { + if (thread_) { // TODO(benvanik): platform kill XELOGE("Thread disposed without exiting"); } @@ -130,7 +129,11 @@ void XThread::set_last_error(uint32_t error_code) { void XThread::set_name(const std::string& name) { name_ = name; - xe::threading::set_name(thread_handle_, name); + if (thread_) { + // May be getting set before the thread is created. + // One the thread is ready it will handle it. + thread_->set_name(name); + } } uint8_t GetFakeCpuNumber(uint8_t proc_mask) { @@ -318,21 +321,45 @@ X_STATUS XThread::Create() { xe::store_and_swap(p + 0x16C, creation_params_.creation_flags); xe::store_and_swap(p + 0x17C, 1); - X_STATUS return_code = PlatformCreate(); - if (XFAILED(return_code)) { - XELOGW("Unable to create platform thread (%.8X)", return_code); - return return_code; - } + // Always retain when starting - the thread owns itself until exited. + Retain(); - // uint32_t proc_mask = creation_params_.creation_flags >> 24; - if (proc_mask) { - SetAffinity(proc_mask); + xe::threading::Thread::CreationParameters params; + params.stack_size = 16 * 1024 * 1024; // Ignore game, always big! + params.create_suspended = (creation_params_.creation_flags & 0x1) == 0x1; + thread_ = xe::threading::Thread::Create(params, [this]() { + // Set name immediately, if we have one. + thread_->set_name(name()); + + // Profiler needs to know about the thread. + xe::Profiler::ThreadEnter(name().c_str()); + + // Execute user code. + current_thread_tls = this; + Execute(); + current_thread_tls = nullptr; + + xe::Profiler::ThreadExit(); + + // Release the self-reference to the thread. + Release(); + }); + if (!thread_) { + // TODO(benvanik): translate error? + XELOGE("CreateThread failed"); + return X_STATUS_NO_MEMORY; + } + thread_->set_affinity_mask(proc_mask); + + if (creation_params_.creation_flags & 0x60) { + thread_->set_priority(creation_params_.creation_flags & 0x20 ? 1 : 0); } return X_STATUS_SUCCESS; } X_STATUS XThread::Exit(int exit_code) { + // This may only be called on the thread itself. assert_true(XThread::GetCurrentThread() == this); // TODO(benvanik): set exit code in thread state block @@ -348,10 +375,9 @@ X_STATUS XThread::Exit(int exit_code) { running_ = false; Release(); - X_STATUS return_code = PlatformExit(exit_code); - if (XFAILED(return_code)) { - return return_code; - } + + // NOTE: this does not return! + thread_->Exit(exit_code); return X_STATUS_SUCCESS; } @@ -360,135 +386,11 @@ X_STATUS XThread::Terminate(int exit_code) { running_ = false; Release(); - X_STATUS status = PlatformTerminate(exit_code); - if (XFAILED(status)) { - return status; - } + thread_->Terminate(exit_code); return X_STATUS_SUCCESS; } -#if XE_PLATFORM_WIN32 - -static uint32_t __stdcall XThreadStartCallbackWin32(void* param) { - auto thread = reinterpret_cast(param); - thread->set_name(thread->name()); - xe::Profiler::ThreadEnter(thread->name().c_str()); - current_thread_tls = thread; - thread->Execute(); - current_thread_tls = nullptr; - xe::Profiler::ThreadExit(); - thread->Release(); - return 0; -} - -X_STATUS XThread::PlatformCreate() { - Retain(); - bool suspended = creation_params_.creation_flags & 0x1; - const size_t kStackSize = 16 * 1024 * 1024; // let's do the stupid thing - thread_handle_ = CreateThread( - NULL, kStackSize, (LPTHREAD_START_ROUTINE)XThreadStartCallbackWin32, this, - suspended ? CREATE_SUSPENDED : 0, NULL); - if (!thread_handle_) { - uint32_t last_error = GetLastError(); - // TODO(benvanik): translate? - XELOGE("CreateThread failed with %d", last_error); - return last_error; - } - - if (creation_params_.creation_flags & 0x60) { - SetThreadPriority(thread_handle_, - creation_params_.creation_flags & 0x20 ? 1 : 0); - } - - return X_STATUS_SUCCESS; -} - -void XThread::PlatformDestroy() { - CloseHandle(reinterpret_cast(thread_handle_)); - thread_handle_ = NULL; -} - -X_STATUS XThread::PlatformExit(int exit_code) { - // NOTE: does not return. - ExitThread(exit_code); - return X_STATUS_SUCCESS; -} - -X_STATUS XThread::PlatformTerminate(int exit_code) { - if (!TerminateThread(thread_handle_, exit_code)) { - return X_STATUS_UNSUCCESSFUL; - } - - return X_STATUS_SUCCESS; -} - -#else - -static void* XThreadStartCallbackPthreads(void* param) { - auto thread = object_ref(reinterpret_cast(param)); - xe::Profiler::ThreadEnter(thread->name().c_str()); - current_thread_tls = thread.get(); - thread->Execute(); - current_thread_tls = nullptr; - xe::Profiler::ThreadExit(); - return 0; -} - -X_STATUS XThread::PlatformCreate() { - pthread_attr_t attr; - - pthread_attr_init(&attr); - // TODO(benvanik): this shouldn't be necessary - // pthread_attr_setstacksize(&attr, creation_params_.stack_size); - - int result_code; - if (creation_params_.creation_flags & 0x1) { -#if XE_PLATFORM_MAC - result_code = pthread_create_suspended_np( - reinterpret_cast(&thread_handle_), &attr, - &XThreadStartCallbackPthreads, this); -#else - // TODO(benvanik): pthread_create_suspended_np on linux - assert_always(); -#endif // OSX - } else { - result_code = pthread_create(reinterpret_cast(&thread_handle_), - &attr, &XThreadStartCallbackPthreads, this); - } - - pthread_attr_destroy(&attr); - - switch (result_code) { - case 0: - // Succeeded! - return X_STATUS_SUCCESS; - default: - case EAGAIN: - return X_STATUS_NO_MEMORY; - case EINVAL: - case EPERM: - return X_STATUS_INVALID_PARAMETER; - } -} - -void XThread::PlatformDestroy() { - // No-op? -} - -X_STATUS XThread::PlatformExit(int exit_code) { - // NOTE: does not return. - pthread_exit(reinterpret_cast(exit_code)); - return X_STATUS_SUCCESS; -} - -X_STATUS XThread::PlatformTerminate(int exit_code) { - // TODO! - assert_always(); -} - -#endif // WIN32 - void XThread::Execute() { XELOGKERNEL("XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X)", thread_id_, handle(), name_.c_str(), @@ -540,7 +442,7 @@ uint32_t XThread::RaiseIrql(uint32_t new_irql) { void XThread::LowerIrql(uint32_t new_irql) { irql_ = new_irql; } -void XThread::CheckApcs() { DeliverAPCs(this); } +void XThread::CheckApcs() { DeliverAPCs(); } void XThread::LockApc() { apc_lock_.lock(); } @@ -548,8 +450,7 @@ void XThread::UnlockApc(bool queue_delivery) { bool needs_apc = apc_list_->HasPending(); apc_lock_.unlock(); if (needs_apc && queue_delivery) { - QueueUserAPC(reinterpret_cast(DeliverAPCs), thread_handle_, - reinterpret_cast(this)); + thread_->QueueUserCallback([this]() { DeliverAPCs(); }); } } @@ -577,21 +478,16 @@ void XThread::EnqueueApc(uint32_t normal_routine, uint32_t normal_context, UnlockApc(true); } -void XThread::DeliverAPCs(void* data) { - XThread* thread = reinterpret_cast(data); - assert_true(XThread::GetCurrentThread() == thread); - +void XThread::DeliverAPCs() { // http://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590?pgno=1 // http://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590?pgno=7 - auto memory = thread->memory(); - auto processor = thread->kernel_state()->processor(); - auto apc_list = thread->apc_list(); - thread->LockApc(); - while (apc_list->HasPending()) { + auto processor = kernel_state()->processor(); + LockApc(); + while (apc_list_->HasPending()) { // Get APC entry (offset for LIST_ENTRY offset) and cache what we need. // Calling the routine may delete the memory/overwrite it. - uint32_t apc_ptr = apc_list->Shift() - 8; - auto apc = reinterpret_cast(memory->TranslateVirtual(apc_ptr)); + uint32_t apc_ptr = apc_list_->Shift() - 8; + auto apc = reinterpret_cast(memory()->TranslateVirtual(apc_ptr)); bool needs_freeing = apc->kernel_routine == XAPC::kDummyKernelRoutine; XELOGD("Delivering APC to %.8X", uint32_t(apc->normal_routine)); @@ -603,7 +499,7 @@ void XThread::DeliverAPCs(void* data) { // The routine can modify all of its arguments before passing it on. // Since we need to give guest accessible pointers over, we copy things // into and out of scratch. - uint8_t* scratch_ptr = memory->TranslateVirtual(thread->scratch_address_); + uint8_t* scratch_ptr = memory()->TranslateVirtual(scratch_address_); xe::store_and_swap(scratch_ptr + 0, apc->normal_routine); xe::store_and_swap(scratch_ptr + 4, apc->normal_context); xe::store_and_swap(scratch_ptr + 8, apc->arg1); @@ -612,11 +508,11 @@ void XThread::DeliverAPCs(void* data) { // kernel_routine(apc_address, &normal_routine, &normal_context, // &system_arg1, &system_arg2) uint64_t kernel_args[] = { - apc_ptr, thread->scratch_address_ + 0, thread->scratch_address_ + 4, - thread->scratch_address_ + 8, thread->scratch_address_ + 12, + apc_ptr, scratch_address_ + 0, scratch_address_ + 4, + scratch_address_ + 8, scratch_address_ + 12, }; - processor->Execute(thread->thread_state(), apc->kernel_routine, - kernel_args, xe::countof(kernel_args)); + processor->Execute(thread_state_, apc->kernel_routine, kernel_args, + xe::countof(kernel_args)); } uint32_t normal_routine = xe::load_and_swap(scratch_ptr + 0); uint32_t normal_context = xe::load_and_swap(scratch_ptr + 4); @@ -626,22 +522,22 @@ void XThread::DeliverAPCs(void* data) { // Call the normal routine. Note that it may have been killed by the kernel // routine. if (normal_routine) { - thread->UnlockApc(false); + UnlockApc(false); // normal_routine(normal_context, system_arg1, system_arg2) uint64_t normal_args[] = {normal_context, arg1, arg2}; - processor->Execute(thread->thread_state(), normal_routine, normal_args, + processor->Execute(thread_state_, normal_routine, normal_args, xe::countof(normal_args)); - thread->LockApc(); + LockApc(); } XELOGD("Completed delivery of APC to %.8X", uint32_t(apc->normal_routine)); // If special, free it. if (needs_freeing) { - memory->SystemHeapFree(apc_ptr); + memory()->SystemHeapFree(apc_ptr); } } - thread->UnlockApc(true); + UnlockApc(true); } void XThread::RundownAPCs() { @@ -675,24 +571,24 @@ void XThread::RundownAPCs() { UnlockApc(true); } -int32_t XThread::QueryPriority() { return GetThreadPriority(thread_handle_); } +int32_t XThread::QueryPriority() { return thread_->priority(); } void XThread::SetPriority(int32_t increment) { priority_ = increment; - int target_priority = 0; + int32_t target_priority = 0; if (increment > 0x22) { - target_priority = THREAD_PRIORITY_HIGHEST; + target_priority = xe::threading::ThreadPriority::kHighest; } else if (increment > 0x11) { - target_priority = THREAD_PRIORITY_ABOVE_NORMAL; + target_priority = xe::threading::ThreadPriority::kAboveNormal; } else if (increment < -0x22) { - target_priority = THREAD_PRIORITY_IDLE; + target_priority = xe::threading::ThreadPriority::kLowest; } else if (increment < -0x11) { - target_priority = THREAD_PRIORITY_LOWEST; + target_priority = xe::threading::ThreadPriority::kBelowNormal; } else { - target_priority = THREAD_PRIORITY_NORMAL; + target_priority = xe::threading::ThreadPriority::kNormal; } if (!FLAGS_ignore_thread_priorities) { - SetThreadPriority(thread_handle_, target_priority); + thread_->set_priority(target_priority); } } @@ -707,15 +603,13 @@ void XThread::SetAffinity(uint32_t affinity) { // 5 - core 2, thread 1 - user // TODO(benvanik): implement better thread distribution. // NOTE: these are logical processors, not physical processors or cores. - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - if (system_info.dwNumberOfProcessors < 6) { + if (xe::threading::logical_processor_count() < 6) { XELOGW("Too few processors - scheduling will be wonky"); } SetActiveCpu(GetFakeCpuNumber(affinity)); affinity_ = affinity; if (!FLAGS_ignore_thread_affinities) { - SetThreadAffinityMask(reinterpret_cast(thread_handle_), affinity); + thread_->set_affinity_mask(affinity); } } @@ -730,11 +624,7 @@ void XThread::SetActiveCpu(uint32_t cpu_index) { } X_STATUS XThread::Resume(uint32_t* out_suspend_count) { - DWORD result = ResumeThread(thread_handle_); - if (result >= 0) { - if (out_suspend_count) { - *out_suspend_count = result; - } + if (thread_->Resume(out_suspend_count)) { return X_STATUS_SUCCESS; } else { return X_STATUS_UNSUCCESSFUL; @@ -742,11 +632,7 @@ X_STATUS XThread::Resume(uint32_t* out_suspend_count) { } X_STATUS XThread::Suspend(uint32_t* out_suspend_count) { - DWORD result = SuspendThread(thread_handle_); - if (result >= 0) { - if (out_suspend_count) { - *out_suspend_count = result; - } + if (thread_->Suspend(out_suspend_count)) { return X_STATUS_SUCCESS; } else { return X_STATUS_UNSUCCESSFUL; @@ -756,7 +642,7 @@ X_STATUS XThread::Suspend(uint32_t* out_suspend_count) { X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable, uint64_t interval) { int64_t timeout_ticks = interval; - DWORD timeout_ms; + uint32_t timeout_ms; if (timeout_ticks > 0) { // Absolute time, based on January 1, 1601. // TODO(benvanik): convert time to relative time. @@ -764,24 +650,27 @@ X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable, timeout_ms = 0; } else if (timeout_ticks < 0) { // Relative time. - timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS + timeout_ms = uint32_t(-timeout_ticks / 10000); // Ticks -> MS } else { timeout_ms = 0; } timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms); - DWORD result = SleepEx(timeout_ms, alertable ? TRUE : FALSE); - switch (result) { - case 0: - return X_STATUS_SUCCESS; - case WAIT_IO_COMPLETION: - return X_STATUS_USER_APC; - default: - return X_STATUS_ALERTED; + if (alertable) { + auto result = + xe::threading::AlertableSleep(std::chrono::milliseconds(timeout_ms)); + switch (result) { + default: + case xe::threading::SleepResult::kSuccess: + return X_STATUS_SUCCESS; + case xe::threading::SleepResult::kAlerted: + return X_STATUS_USER_APC; + } + } else { + xe::threading::Sleep(std::chrono::milliseconds(timeout_ms)); + return X_STATUS_SUCCESS; } } -void* XThread::GetWaitHandle() { return thread_handle_; } - XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size, uint32_t creation_flags, std::function host_fn) : XThread(kernel_state, stack_size, 0, 0, 0, creation_flags, false), diff --git a/src/xenia/kernel/objects/xthread.h b/src/xenia/kernel/objects/xthread.h index 8793381e7..95b3719cf 100644 --- a/src/xenia/kernel/objects/xthread.h +++ b/src/xenia/kernel/objects/xthread.h @@ -14,6 +14,7 @@ #include #include +#include "xenia/base/threading.h" #include "xenia/cpu/thread_state.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -133,15 +134,10 @@ class XThread : public XObject { X_STATUS Delay(uint32_t processor_mode, uint32_t alertable, uint64_t interval); - virtual void* GetWaitHandle(); + xe::threading::WaitHandle* GetWaitHandle() override { return thread_.get(); } protected: - X_STATUS PlatformCreate(); - void PlatformDestroy(); - X_STATUS PlatformExit(int exit_code); - X_STATUS PlatformTerminate(int exit_code); - - static void DeliverAPCs(void* data); + void DeliverAPCs(); void RundownAPCs(); struct { @@ -153,7 +149,7 @@ class XThread : public XObject { } creation_params_; uint32_t thread_id_; - void* thread_handle_; + std::unique_ptr thread_; uint32_t scratch_address_; uint32_t scratch_size_; uint32_t tls_address_; diff --git a/src/xenia/kernel/objects/xtimer.cc b/src/xenia/kernel/objects/xtimer.cc index b8abe6748..b16ac4c9b 100644 --- a/src/xenia/kernel/objects/xtimer.cc +++ b/src/xenia/kernel/objects/xtimer.cc @@ -16,32 +16,25 @@ namespace xe { namespace kernel { -XTimer::XTimer(KernelState* kernel_state) - : XObject(kernel_state, kTypeTimer), native_handle_(NULL) {} +XTimer::XTimer(KernelState* kernel_state) : XObject(kernel_state, kTypeTimer) {} -XTimer::~XTimer() { - if (native_handle_) { - CloseHandle(native_handle_); - } -} +XTimer::~XTimer() = default; void XTimer::Initialize(uint32_t timer_type) { - assert_null(native_handle_); + assert_false(timer_); bool manual_reset = false; switch (timer_type) { case 0: // NotificationTimer - manual_reset = true; + timer_ = xe::threading::Timer::CreateManualResetTimer(); break; case 1: // SynchronizationTimer - manual_reset = false; + timer_ = xe::threading::Timer::CreateSynchronizationTimer(); break; default: assert_always(); break; } - - native_handle_ = CreateWaitableTimer(NULL, manual_reset, NULL); } X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms, @@ -54,34 +47,29 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms, // 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); - LARGE_INTEGER due_time_li; - due_time_li.QuadPart = due_time; - BOOL result = - SetWaitableTimer(native_handle_, &due_time_li, period_ms, - routine ? (PTIMERAPCROUTINE)CompletionRoutine : NULL, - this, resume ? TRUE : FALSE); + bool result; + if (!period_ms) { + result = timer_->SetOnce(std::chrono::nanoseconds(due_time * 100)); + } else { + result = timer_->SetRepeating(std::chrono::nanoseconds(due_time * 100), + std::chrono::milliseconds(period_ms)); + } return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; } -void XTimer::CompletionRoutine(XTimer* timer, DWORD timer_low, - DWORD timer_high) { - assert_true(timer->current_routine_); - - // Queue APC to call back routine with (arg, low, high). - // TODO(benvanik): APC dispatch. - XELOGE("Timer needs APC!"); - - DebugBreak(); -} - X_STATUS XTimer::Cancel() { - return CancelWaitableTimer(native_handle_) == 0 ? X_STATUS_SUCCESS - : X_STATUS_UNSUCCESSFUL; + return timer_->Cancel() ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; } } // namespace kernel diff --git a/src/xenia/kernel/objects/xtimer.h b/src/xenia/kernel/objects/xtimer.h index 4203d7dc0..59dcc1337 100644 --- a/src/xenia/kernel/objects/xtimer.h +++ b/src/xenia/kernel/objects/xtimer.h @@ -10,7 +10,7 @@ #ifndef XENIA_KERNEL_XBOXKRNL_XTIMER_H_ #define XENIA_KERNEL_XBOXKRNL_XTIMER_H_ -#include "xenia/base/platform_win.h" +#include "xenia/base/threading.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -20,7 +20,7 @@ namespace kernel { class XTimer : public XObject { public: XTimer(KernelState* kernel_state); - virtual ~XTimer(); + ~XTimer() override; void Initialize(uint32_t timer_type); @@ -29,16 +29,13 @@ class XTimer : public XObject { uint32_t routine_arg, bool resume); X_STATUS Cancel(); - virtual void* GetWaitHandle() { return native_handle_; } + xe::threading::WaitHandle* GetWaitHandle() override { return timer_.get(); } private: - HANDLE native_handle_; + std::unique_ptr timer_; uint32_t current_routine_; uint32_t current_routine_arg_; - - static void CompletionRoutine(XTimer* timer, DWORD timer_low, - DWORD timer_high); }; } // namespace kernel diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index 61a6d479c..4d5a9f332 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -117,28 +117,30 @@ uint32_t XObject::TimeoutTicksToMs(int64_t timeout_ticks) { X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout) { - void* wait_handle = GetWaitHandle(); + auto wait_handle = GetWaitHandle(); if (!wait_handle) { // Object doesn't support waiting. return X_STATUS_SUCCESS; } - DWORD timeout_ms = opt_timeout ? TimeoutTicksToMs(*opt_timeout) : INFINITE; - timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms); + auto timeout_ms = + opt_timeout ? std::chrono::milliseconds(Clock::ScaleGuestDurationMillis( + TimeoutTicksToMs(*opt_timeout))) + : std::chrono::milliseconds::max(); - DWORD result = WaitForSingleObjectEx(wait_handle, timeout_ms, alertable); + auto result = wait_handle->Wait(alertable ? true : false, timeout_ms); switch (result) { - case WAIT_OBJECT_0: + case xe::threading::WaitResult::kSuccess: return X_STATUS_SUCCESS; - case WAIT_IO_COMPLETION: + case xe::threading::WaitResult::kUserCallback: // Or X_STATUS_ALERTED? return X_STATUS_USER_APC; - case WAIT_TIMEOUT: + case xe::threading::WaitResult::kTimeout: YieldProcessor(); return X_STATUS_TIMEOUT; default: - case WAIT_FAILED: - case WAIT_ABANDONED: + case xe::threading::WaitResult::kAbandoned: + case xe::threading::WaitResult::kFailed: return X_STATUS_ABANDONED_WAIT_0; } } @@ -146,33 +148,81 @@ X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, X_STATUS XObject::SignalAndWait(XObject* signal_object, XObject* wait_object, uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout) { - DWORD timeout_ms = opt_timeout ? TimeoutTicksToMs(*opt_timeout) : INFINITE; - timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms); + auto timeout_ms = + opt_timeout ? std::chrono::milliseconds(Clock::ScaleGuestDurationMillis( + TimeoutTicksToMs(*opt_timeout))) + : std::chrono::milliseconds::max(); - DWORD result = SignalObjectAndWait(signal_object->GetWaitHandle(), - wait_object->GetWaitHandle(), timeout_ms, - alertable ? TRUE : FALSE); - - return result; + auto result = xe::threading::WaitHandle::SignalAndWait( + signal_object->GetWaitHandle(), wait_object->GetWaitHandle(), + alertable ? true : false, timeout_ms); + switch (result) { + case xe::threading::WaitResult::kSuccess: + return X_STATUS_SUCCESS; + case xe::threading::WaitResult::kUserCallback: + // Or X_STATUS_ALERTED? + return X_STATUS_USER_APC; + case xe::threading::WaitResult::kTimeout: + YieldProcessor(); + return X_STATUS_TIMEOUT; + default: + case xe::threading::WaitResult::kAbandoned: + case xe::threading::WaitResult::kFailed: + return X_STATUS_ABANDONED_WAIT_0; + } } X_STATUS XObject::WaitMultiple(uint32_t count, XObject** objects, uint32_t wait_type, uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout) { - HANDLE* wait_handles = (HANDLE*)alloca(sizeof(HANDLE) * count); - for (uint32_t n = 0; n < count; n++) { - wait_handles[n] = objects[n]->GetWaitHandle(); - assert_not_null(wait_handles[n]); + std::vector wait_handles(count); + for (size_t i = 0; i < count; ++i) { + wait_handles[i] = objects[i]->GetWaitHandle(); + assert_not_null(wait_handles[i]); } - DWORD timeout_ms = opt_timeout ? TimeoutTicksToMs(*opt_timeout) : INFINITE; - timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms); + auto timeout_ms = + opt_timeout ? std::chrono::milliseconds(Clock::ScaleGuestDurationMillis( + TimeoutTicksToMs(*opt_timeout))) + : std::chrono::milliseconds::max(); - DWORD result = WaitForMultipleObjectsEx( - count, wait_handles, wait_type ? FALSE : TRUE, timeout_ms, alertable); - - return result; + if (wait_type) { + auto result = xe::threading::WaitHandle::WaitAny( + std::move(wait_handles), alertable ? true : false, timeout_ms); + switch (result.first) { + case xe::threading::WaitResult::kSuccess: + return X_STATUS(result.second); + case xe::threading::WaitResult::kUserCallback: + // Or X_STATUS_ALERTED? + return X_STATUS_USER_APC; + case xe::threading::WaitResult::kTimeout: + YieldProcessor(); + return X_STATUS_TIMEOUT; + default: + case xe::threading::WaitResult::kAbandoned: + return X_STATUS(X_STATUS_ABANDONED_WAIT_0 + result.second); + case xe::threading::WaitResult::kFailed: + return X_STATUS_UNSUCCESSFUL; + } + } else { + auto result = xe::threading::WaitHandle::WaitAll( + std::move(wait_handles), alertable ? true : false, timeout_ms); + switch (result) { + case xe::threading::WaitResult::kSuccess: + return X_STATUS_SUCCESS; + case xe::threading::WaitResult::kUserCallback: + // Or X_STATUS_ALERTED? + return X_STATUS_USER_APC; + case xe::threading::WaitResult::kTimeout: + YieldProcessor(); + return X_STATUS_TIMEOUT; + default: + case xe::threading::WaitResult::kAbandoned: + case xe::threading::WaitResult::kFailed: + return X_STATUS_ABANDONED_WAIT_0; + } + } } uint8_t* XObject::CreateNative(uint32_t size) { diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 26412c997..18cdaa71c 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -12,6 +12,7 @@ #include +#include "xenia/base/threading.h" #include "xenia/xbox.h" namespace xe { @@ -148,7 +149,7 @@ class XObject { GetNativeObject(kernel_state, native_ptr, as_type).release())); } - virtual void* GetWaitHandle() { return 0; } + virtual xe::threading::WaitHandle* GetWaitHandle() { return nullptr; } protected: // Creates the kernel object for guest code to use. Typically not needed.