From 32b8c341eef3b84fd1965b3398eb2dac7c16b316 Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Sun, 12 Oct 2025 20:30:29 +0900 Subject: [PATCH] [Kernel] ReleaseSemaphore for Posix to match Windows semantics --- src/xenia/base/threading_posix.cc | 15 ++++++++------- src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc | 17 +++++++++++++++-- src/xenia/kernel/xsemaphore.cc | 10 +++++++--- src/xenia/kernel/xsemaphore.h | 3 ++- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index c8549ecfb..a2c634d08 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -320,14 +320,15 @@ class PosixCondition final : public PosixConditionBase { bool Signal() override { return Release(1, nullptr); } bool Release(uint32_t release_count, int* out_previous_count) { - if (maximum_count_ - count_ >= release_count) { - auto lock = std::unique_lock(mutex_); - if (out_previous_count) *out_previous_count = count_; - count_ += release_count; - cond_.notify_all(); - return true; + auto lock = std::unique_lock(mutex_); + // Validate that releasing would not exceed the maximum count + if (count_ + release_count > maximum_count_) { + return false; } - return false; + if (out_previous_count) *out_previous_count = count_; + count_ += release_count; + cond_.notify_all(); + return true; } private: diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index e6fa86ba1..fba756fee 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -718,7 +718,10 @@ uint32_t xeKeReleaseSemaphore(X_KSEMAPHORE* semaphore_ptr, uint32_t increment, // TODO(benvanik): increment thread priority? // TODO(benvanik): wait? - return sem->ReleaseSemaphore(adjustment); + int32_t previous_count = 0; + [[maybe_unused]] bool success = + sem->ReleaseSemaphore(adjustment, &previous_count); + return static_cast(previous_count); } dword_result_t KeReleaseSemaphore_entry(pointer_t semaphore_ptr, @@ -777,7 +780,17 @@ dword_result_t NtReleaseSemaphore_entry(dword_t sem_handle, auto sem = kernel_state()->object_table()->LookupObject(sem_handle); if (sem) { - previous_count = sem->ReleaseSemaphore((int32_t)release_count); + bool success = + sem->ReleaseSemaphore((int32_t)release_count, &previous_count); + if (!success) { + // Releasing would exceed the semaphore's maximum count + // Windows returns STATUS_SEMAPHORE_LIMIT_EXCEEDED (0x0000012B) + XELOGW( + "NtReleaseSemaphore: release_count={} would exceed maximum (current " + "count={})", + uint32_t(release_count), previous_count); + result = 0x0000012B; + } } else { result = X_STATUS_INVALID_HANDLE; } diff --git a/src/xenia/kernel/xsemaphore.cc b/src/xenia/kernel/xsemaphore.cc index 34b960b25..f04d0826e 100644 --- a/src/xenia/kernel/xsemaphore.cc +++ b/src/xenia/kernel/xsemaphore.cc @@ -40,10 +40,14 @@ bool XSemaphore::InitializeNative(void* native_ptr, X_DISPATCH_HEADER* header) { return !!semaphore_; } -int32_t XSemaphore::ReleaseSemaphore(int32_t release_count) { +bool XSemaphore::ReleaseSemaphore(int32_t release_count, + int32_t* out_previous_count) { int32_t previous_count = 0; - semaphore_->Release(release_count, &previous_count); - return previous_count; + bool success = semaphore_->Release(release_count, &previous_count); + if (out_previous_count) { + *out_previous_count = previous_count; + } + return success; } bool XSemaphore::Save(ByteStream* stream) { diff --git a/src/xenia/kernel/xsemaphore.h b/src/xenia/kernel/xsemaphore.h index 77278af9f..2135fdec3 100644 --- a/src/xenia/kernel/xsemaphore.h +++ b/src/xenia/kernel/xsemaphore.h @@ -29,7 +29,8 @@ class XSemaphore : public XObject { [[nodiscard]] bool InitializeNative(void* native_ptr, X_DISPATCH_HEADER* header); - int32_t ReleaseSemaphore(int32_t release_count); + [[nodiscard]] bool ReleaseSemaphore(int32_t release_count, + int32_t* out_previous_count); bool Save(ByteStream* stream) override; static object_ref Restore(KernelState* kernel_state,