diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index 5428a7907..a176709ba 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -861,6 +861,45 @@ SHIM_CALL NtWaitForSingleObjectEx_shim( } +SHIM_CALL KeWaitForMultipleObjects_shim( + PPCContext* ppc_state, KernelState* state) { + uint32_t count = SHIM_GET_ARG_32(0); + uint32_t objects_ptr = SHIM_GET_ARG_32(1); + uint32_t wait_type = SHIM_GET_ARG_32(2); + uint32_t wait_reason = SHIM_GET_ARG_32(3); + uint32_t processor_mode = SHIM_GET_ARG_32(4); + uint32_t alertable = SHIM_GET_ARG_32(5); + uint32_t timeout_ptr = SHIM_GET_ARG_32(6); + uint32_t wait_block_array_ptr = SHIM_GET_ARG_32(7); + + XELOGD( + "KeWaitForMultipleObjects(%d, %.8X, %.8X, %.8X, %.8X, %.1X, %.8X, %.8X)", + count, objects_ptr, wait_type, wait_reason, processor_mode, + alertable, timeout_ptr, wait_block_array_ptr); + + X_STATUS result = X_STATUS_SUCCESS; + + XObject** objects = (XObject**)alloca(sizeof(XObject*) * count); + for (uint32_t n = 0; n < count; n++) { + uint32_t object_ptr_ptr = SHIM_MEM_32(objects_ptr + n * 4); + void* object_ptr = SHIM_MEM_ADDR(object_ptr_ptr); + objects[n] = XObject::GetObject(state, object_ptr); + if (!objects[n]) { + SHIM_SET_RETURN(X_STATUS_INVALID_PARAMETER); + return; + } + } + + uint64_t timeout = timeout_ptr ? SHIM_MEM_64(timeout_ptr) : 0; + result = XObject::WaitMultiple( + count, objects, + wait_type, wait_reason, processor_mode, alertable, + timeout_ptr ? &timeout : NULL); + + SHIM_SET_RETURN(result); +} + + uint32_t xeKfAcquireSpinLock(void* lock_ptr) { // Lock. while (!xe_atomic_cas_32(0, 1, lock_ptr)) { @@ -975,6 +1014,7 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports( SHIM_SET_MAPPING("xboxkrnl.exe", KeWaitForSingleObject, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtWaitForSingleObjectEx, state); + SHIM_SET_MAPPING("xboxkrnl.exe", KeWaitForMultipleObjects, state); SHIM_SET_MAPPING("xboxkrnl.exe", KfAcquireSpinLock, state); SHIM_SET_MAPPING("xboxkrnl.exe", KfReleaseSpinLock, state); diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index f659cbe72..175bc76ff 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -70,6 +70,22 @@ X_STATUS XObject::Delete() { return shared_kernel_state_->object_table()->RemoveHandle(handle_); } +namespace { +uint32_t ConvertTimeoutTicks(int64_t timeout_ticks) { + if (timeout_ticks > 0) { + // Absolute time, based on January 1, 1601. + // TODO(benvanik): convert time to relative time. + XEASSERTALWAYS(); + return 0; + } else if (timeout_ticks < 0) { + // Relative time. + return (uint32_t)(-timeout_ticks / 10000); // Ticks -> MS + } else { + return 0; + } +} +} + X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout) { void* wait_handle = GetWaitHandle(); @@ -78,23 +94,8 @@ X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, return X_STATUS_SUCCESS; } - DWORD timeout_ms; - if (opt_timeout) { - int64_t timeout_ticks = (int64_t)(*opt_timeout); - if (timeout_ticks > 0) { - // Absolute time, based on January 1, 1601. - // TODO(benvanik): convert time to relative time. - XEASSERTALWAYS(); - timeout_ms = 0; - } else if (timeout_ticks < 0) { - // Relative time. - timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS - } else { - timeout_ms = 0; - } - } else { - timeout_ms = INFINITE; - } + DWORD timeout_ms = opt_timeout ? + ConvertTimeoutTicks(*opt_timeout) : INFINITE; DWORD result = WaitForSingleObjectEx(wait_handle, timeout_ms, alertable); switch (result) { @@ -112,6 +113,26 @@ X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode, } } +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) { + void** wait_handles = (void**)alloca(sizeof(void*) * count); + for (uint32_t n = 0; n < count; n++) { + wait_handles[n] = objects[n]->GetWaitHandle(); + XEASSERTNOTNULL(wait_handles[n]); + } + + DWORD timeout_ms = opt_timeout ? + ConvertTimeoutTicks(*opt_timeout) : INFINITE; + + DWORD result = WaitForMultipleObjectsEx( + count, wait_handles, + wait_type ? TRUE : FALSE, timeout_ms, alertable); + + return result; +} + void XObject::LockType() { xe_mutex_lock(shared_kernel_state_->object_mutex_); } diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index b4731bb0f..9ff688ae6 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -60,6 +60,10 @@ public: X_STATUS Wait( uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout); + static X_STATUS 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); static void LockType(); static void UnlockType();