Merge pull request #3691 from MarcZX100/fix/sem-timedwait-available-first
fix(libkernel): handle available sem_timedwait counts first
This commit is contained in:
commit
6426baa107
|
|
@ -84,6 +84,14 @@ int APS5_VABI sem_trywait_nid_postfix(void* sem) {
|
|||
int APS5_VABI sem_timedwait_nid_postfix(void* sem, const KernelTimespec* abstime) {
|
||||
auto* semaphore = Get(sem);
|
||||
if (!semaphore) return Fail(PosixThread::GUEST_EINVAL);
|
||||
{
|
||||
std::lock_guard lock(semaphore->lock);
|
||||
if (semaphore->count > 0) {
|
||||
--semaphore->count;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!abstime) return WaitFor(semaphore, nullptr);
|
||||
KernelUseconds usec = 0;
|
||||
if (!PosixThread::RelativeMicroseconds(GUEST_REALTIME_CLOCK, abstime, &usec)) return Fail(PosixThread::GUEST_EINVAL);
|
||||
return WaitFor(semaphore, &usec);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sem_init_nid_postfix(void*, int, unsigned int);
|
||||
int APS5_VABI sem_destroy_nid_postfix(void*);
|
||||
int APS5_VABI sem_post_nid_postfix(void*);
|
||||
int APS5_VABI sem_trywait_nid_postfix(void*);
|
||||
int APS5_VABI sem_timedwait_nid_postfix(void*, const KernelTimespec*);
|
||||
int APS5_VABI sem_getvalue_nid_postfix(void*, int*);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
|
|
@ -58,4 +62,41 @@ int main(int argc, char** argv) {
|
|||
Require(sem_trywait_nid_postfix(&sem) == 0, "normal wait must succeed");
|
||||
Require(sem_getvalue_nid_postfix(&sem, &value) == 0 && value == 0, "normal wait must decrement");
|
||||
Require(sem_destroy_nid_postfix(&sem) == 0, "normal destroy must succeed");
|
||||
|
||||
KernelTimespec invalidTimeout{0, 1000000000};
|
||||
Require(sem_init_nid_postfix(&sem, 0, 1) == 0, "available semaphore must initialize");
|
||||
Require(sem_timedwait_nid_postfix(&sem, nullptr) == 0, "available semaphore must ignore null timeout");
|
||||
Require(sem_getvalue_nid_postfix(&sem, &value) == 0 && value == 0, "null-timeout wait must consume the available count");
|
||||
Require(sem_destroy_nid_postfix(&sem) == 0, "null-timeout semaphore destroy must succeed");
|
||||
|
||||
Require(sem_init_nid_postfix(&sem, 0, 1) == 0, "invalid-timeout semaphore must initialize");
|
||||
Require(sem_timedwait_nid_postfix(&sem, &invalidTimeout) == 0, "available semaphore must ignore invalid timeout");
|
||||
Require(sem_getvalue_nid_postfix(&sem, &value) == 0 && value == 0, "invalid-timeout wait must consume the available count");
|
||||
Require(sem_destroy_nid_postfix(&sem) == 0, "invalid-timeout semaphore destroy must succeed");
|
||||
|
||||
Require(sem_init_nid_postfix(&sem, 0, 0) == 0, "blocked semaphore must initialize");
|
||||
std::atomic<bool> entered = false;
|
||||
std::atomic<bool> completed = false;
|
||||
int waitResult = -2;
|
||||
std::thread waiter([&] {
|
||||
entered.store(true, std::memory_order_release);
|
||||
waitResult = sem_timedwait_nid_postfix(&sem, nullptr);
|
||||
completed.store(true, std::memory_order_release);
|
||||
});
|
||||
while (!entered.load(std::memory_order_acquire)) std::this_thread::yield();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
Require(!completed.load(std::memory_order_acquire), "blocked null-timeout wait must remain blocked");
|
||||
Require(sem_post_nid_postfix(&sem) == 0, "post must release a null-timeout waiter");
|
||||
waiter.join();
|
||||
Require(waitResult == 0, "null-timeout waiter must succeed after a post");
|
||||
Require(sem_getvalue_nid_postfix(&sem, &value) == 0 && value == 0, "null-timeout waiter must consume the posted count");
|
||||
|
||||
Require(sem_init_nid_postfix(&sem, 0, 0) == 0, "invalid-timeout blocked semaphore must initialize");
|
||||
*__error_nid_postfix() = 0;
|
||||
Require(sem_timedwait_nid_postfix(&sem, &invalidTimeout) == -1 && *__error_nid_postfix() == 22, "blocked invalid-timeout wait must report EINVAL");
|
||||
KernelTimespec expiredTimeout{0, 0};
|
||||
*__error_nid_postfix() = 0;
|
||||
Require(sem_timedwait_nid_postfix(&sem, &expiredTimeout) == -1 && *__error_nid_postfix() == 60, "blocked expired-timeout wait must report ETIMEDOUT");
|
||||
Require(sem_getvalue_nid_postfix(&sem, &value) == 0 && value == 0, "failed timed waits must preserve the count");
|
||||
Require(sem_destroy_nid_postfix(&sem) == 0, "blocked semaphore destroy must succeed");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -370,6 +370,9 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
### Functional
|
||||
|
||||
- [sceAudioInHqOpen, sceAudioInAsyncOpen](../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) open the same port as `sceAudioInOpen`, so `sceAudioInInput`, `sceAudioInGetSilentState` and `sceAudioInClose` accept their handles. The signatures agree across [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/fc5d2cc219c43c8c8f354153e7b3d0c04a5d506e/src/core/libraries/audio/audioin.cpp), [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Audio/AudioIn.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/c86c8b4d9333a766cc8c4495f89f893bf35e6199/src/libs/audio.cpp). HqOpen is limited to 128 samples at 48 kHz; AsyncOpen rejects sizes above 384 and throws for currently unverified inputs. Host capture pacing and console behavior remain unverified.
|
||||
|
||||
- [sem_timedwait](../../core/libs/prx/libkernel/Pthread/Posix/Sem.cpp) (libkernel) consumes an available count before checking the timeout and waits indefinitely for a null timeout, following FreeBSD 11's `sem_timedwait`. Console behavior for null and invalid timeouts when a count is available is unverified.
|
||||
|
||||
- [getc_unlocked, putc_unlocked](../../core/libs/prx/libc/src/StandardStreams.cpp) (libc) - byte I/O uses the host unlocked operations; guest EOF/error flags and macro counters follow `FileStream`. Linux status queries use `feof_unlocked`/`ferror_unlocked` because [glibc `feof`](https://github.com/bminor/glibc/blob/d2097651cc57834dbfcaa102ddfacae0d86cfb66/libio/feof.c) and [`ferror`](https://github.com/bminor/glibc/blob/d2097651cc57834dbfcaa102ddfacae0d86cfb66/libio/ferror.c) can acquire the stream lock. Windows uses [the CRT no-lock byte operations](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fgetc-nolock-fgetwc-nolock) and `feof`/`ferror`, verified under a held stream lock on UCRT. Console behavior and other host CRTs remain unverified.
|
||||
|
||||
- [ftrylockfile](../../core/libs/prx/libc/src/RuntimeSupport.cpp) (libc) - tries the host CRT stream lock that `flockfile`, `funlockfile` and each locking host stdio call on the stream take, so it returns -1 while another thread holds the stream through `flockfile` or is inside such a call, and 0 when it acquires the lock or already owns it (recursively, released by as many `funlockfile` calls), as FreeBSD 11 [`_ftrylockfile`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/_flock_stub.c#L89-L104) does. A libc export holds the lock only for each host call it makes, not across several host calls or while it updates the guest `FILE` prefix. Linux uses glibc `ftrylockfile`. UCRT has no try variant of `_lock_file`, so Windows calls `TryEnterCriticalSection` on the `CRITICAL_SECTION` that `_lock_file` enters, found through the private UCRT stream layout (`__crt_stdio_stream_data` in `ucrt/inc/corecrt_internal_stdio.h`, unchanged from Windows SDK 10.0.19041 to 10.0.26100); another CRT or a change of that layout breaks it. A null or closed stream throws where FreeBSD dereferences it
|
||||
|
|
|
|||
Loading…
Reference in New Issue