diff --git a/libxenia.vcxproj b/libxenia.vcxproj index caabb2478..2b0bfb2b1 100644 --- a/libxenia.vcxproj +++ b/libxenia.vcxproj @@ -224,6 +224,7 @@ + diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters index 1a0c53b94..f927d82d0 100644 --- a/libxenia.vcxproj.filters +++ b/libxenia.vcxproj.filters @@ -404,6 +404,7 @@ + diff --git a/src/xenia/base/mutex.h b/src/xenia/base/mutex.h new file mode 100644 index 000000000..bbe09fc22 --- /dev/null +++ b/src/xenia/base/mutex.h @@ -0,0 +1,92 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_MUTEX_H_ +#define XENIA_BASE_MUTEX_H_ + +#include +#include +#include + +#include "xenia/base/platform.h" + +namespace xe { + +// This type should be interchangable with std::mutex, only it provides the +// ability to resume threads that hold the lock when the debugger needs it. +class mutex { + public: + mutex(int _Flags = 0) noexcept { + _Mtx_init_in_situ(_Mymtx(), _Flags | _Mtx_try); + } + + ~mutex() noexcept { _Mtx_destroy_in_situ(_Mymtx()); } + + mutex(const mutex&) = delete; + mutex& operator=(const mutex&) = delete; + + void lock() { + _Mtx_lock(_Mymtx()); + holding_thread_ = ::GetCurrentThread(); + } + + bool try_lock() { + if (_Mtx_trylock(_Mymtx()) == _Thrd_success) { + holding_thread_ = ::GetCurrentThread(); + return true; + } + return false; + } + + void lock_as_debugger() { + if (_Mtx_trylock(_Mymtx()) == _Thrd_success) { + // Nothing holding it. + return; + } + // Resume the thread holding it, which will unlock and suspend itself. + debugger_waiting_ = true; + ResumeThread(holding_thread_); + lock(); + debugger_waiting_ = false; + } + + void unlock() { + bool debugger_waiting = debugger_waiting_; + _Mtx_unlock(_Mymtx()); + if (debugger_waiting) { + SuspendThread(nullptr); + } + } + + typedef void* native_handle_type; + + native_handle_type native_handle() { return (_Mtx_getconcrtcs(_Mymtx())); } + + private: + std::aligned_storage<_Mtx_internal_imp_size, + _Mtx_internal_imp_alignment>::type _Mtx_storage; + HANDLE holding_thread_; + bool debugger_waiting_; + + _Mtx_t _Mymtx() noexcept { return (reinterpret_cast<_Mtx_t>(&_Mtx_storage)); } +}; + +class recursive_mutex : public mutex { + public: + recursive_mutex() : mutex(_Mtx_recursive) {} + + bool try_lock() noexcept { return mutex::try_lock(); } + + recursive_mutex(const recursive_mutex&) = delete; + recursive_mutex& operator=(const recursive_mutex&) = delete; +}; + +} // namespace xe + +#endif // XENIA_BASE_MUTEX_H_ diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index c6033ba7d..c2b81e64c 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -107,7 +107,7 @@ bool KernelState::IsKernelModule(const char* name) { // Executing module isn't a kernel module. return false; } - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (auto kernel_module : kernel_modules_) { if (kernel_module->Matches(name)) { return true; @@ -125,7 +125,7 @@ XModule* KernelState::GetModule(const char* name) { // Some games request this, for some reason. wtf. return nullptr; } - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (auto kernel_module : kernel_modules_) { if (kernel_module->Matches(name)) { kernel_module->Retain(); @@ -165,7 +165,7 @@ void KernelState::SetExecutableModule(XUserModule* module) { } void KernelState::LoadKernelModule(XKernelModule* kernel_module) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); kernel_modules_.push_back(kernel_module); } @@ -179,7 +179,7 @@ XUserModule* KernelState::LoadUserModule(const char* raw_name) { XUserModule* module = nullptr; { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); // See if we've already loaded it for (XUserModule* existing_module : user_modules_) { @@ -221,12 +221,12 @@ XUserModule* KernelState::LoadUserModule(const char* raw_name) { } void KernelState::RegisterThread(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); threads_by_id_[thread->thread_id()] = thread; } void KernelState::UnregisterThread(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); auto it = threads_by_id_.find(thread->thread_id()); if (it != threads_by_id_.end()) { threads_by_id_.erase(it); @@ -234,7 +234,7 @@ void KernelState::UnregisterThread(XThread* thread) { } void KernelState::OnThreadExecute(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); // Must be called on executing thread. assert_true(XThread::GetCurrentThread() == thread); @@ -257,7 +257,7 @@ void KernelState::OnThreadExecute(XThread* thread) { } void KernelState::OnThreadExit(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); // Must be called on executing thread. assert_true(XThread::GetCurrentThread() == thread); @@ -280,7 +280,7 @@ void KernelState::OnThreadExit(XThread* thread) { } XThread* KernelState::GetThreadByID(uint32_t thread_id) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); XThread* thread = nullptr; auto it = threads_by_id_.find(thread_id); if (it != threads_by_id_.end()) { @@ -292,7 +292,7 @@ XThread* KernelState::GetThreadByID(uint32_t thread_id) { } void KernelState::RegisterNotifyListener(XNotifyListener* listener) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); notify_listeners_.push_back(listener); // Games seem to expect a few notifications on startup, only for the first @@ -316,7 +316,7 @@ void KernelState::RegisterNotifyListener(XNotifyListener* listener) { } void KernelState::UnregisterNotifyListener(XNotifyListener* listener) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (auto it = notify_listeners_.begin(); it != notify_listeners_.end(); ++it) { if (*it == listener) { @@ -327,7 +327,7 @@ void KernelState::UnregisterNotifyListener(XNotifyListener* listener) { } void KernelState::BroadcastNotification(XNotificationID id, uint32_t data) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (auto it = notify_listeners_.begin(); it != notify_listeners_.end(); ++it) { (*it)->EnqueueNotification(id, data); diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index eab953d2c..e3676b888 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -15,6 +15,7 @@ #include #include +#include "xenia/base/mutex.h" #include "xenia/cpu/export_resolver.h" #include "xenia/kernel/app.h" #include "xenia/kernel/content_manager.h" @@ -64,7 +65,7 @@ class KernelState { ContentManager* content_manager() const { return content_manager_.get(); } ObjectTable* object_table() const { return object_table_; } - std::recursive_mutex& object_mutex() { return object_mutex_; } + xe::recursive_mutex& object_mutex() { return object_mutex_; } uint32_t process_type() const { return process_type_; } void set_process_type(uint32_t value) { process_type_ = value; } @@ -115,7 +116,7 @@ class KernelState { std::unique_ptr content_manager_; ObjectTable* object_table_; - std::recursive_mutex object_mutex_; + xe::recursive_mutex object_mutex_; std::unordered_map threads_by_id_; std::vector notify_listeners_; bool has_notified_startup_; diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index d849d4e4d..3451157bc 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -153,7 +153,7 @@ X_STATUS XObject::WaitMultiple(uint32_t count, XObject** objects, } void XObject::SetNativePointer(uint32_t native_ptr, bool uninitialized) { - std::lock_guard lock(kernel_state_->object_mutex()); + std::lock_guard lock(kernel_state_->object_mutex()); auto header = kernel_state_->memory()->TranslateVirtual(native_ptr); @@ -183,7 +183,7 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr, // We identify this by checking the low bit of wait_list_blink - if it's 1, // we have already put our pointer in there. - std::lock_guard lock(kernel_state->object_mutex()); + std::lock_guard lock(kernel_state->object_mutex()); auto header = reinterpret_cast(native_ptr);