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);