QueueForDeliveryDrain ran after ResumeThread with no coordination with
the target's final ReapDeliveryDrain: a target could exit between resume
and enqueue, leaving the post in a box nothing would drain again and
retaining the QueuedDelivery reference.
Admission is now decided under drainBoxesLock against the target's
_finished flag, which is stored before the final reap in program order;
denied posts run their callback inline so the entry and the reference
are resolved exactly once on either path.
The GuestRaiseException regression arms a post hook, holds the producer
between resume and enqueue, lets the target finish and reap, then
completes the post and requires the drain to stay empty; the churn
rounds also assert the post hook observed mailbox posts alongside the
delivered raises.
The conflict in core/libs/tests/GuestRudpContextLifecycle.cpp with #13799 keeps both changes: this branch's removal of the check that sceRudpActivate throws, and main's sceRudpBind(ctx, 5, 0, 2) call after it.
On Windows a sleep shorter than TimedWait's 0.5 ms lead, and the last 0.5 ms of a longer one, is a YieldProcessor spin in host code with no wait counted. A raise that found the target there could only be delivered through the return-address breakpoint or sampling, so sceKernelRaiseException at a thread looping on such sleeps was lost: a thread looping on sceKernelUsleep(200) lost the first raise in 5 of 5 runs.
The spins in SleepUntil and SleepNanosUnlessChanged now run under a DeliveryDeferral and drain APCs afterwards, as BeginPark and Unpark already do. A raise that finds the target spinning is queued as an APC and runs when the sleep ends.
GuestRaiseShortSleep raises 100 times each at a thread sleeping 100, 200 and 400 us in a loop, with sleep wakes on (SleepNanosUnlessChanged) and with APS5_NO_SLEEP_WAKE (SleepUntil). Both fail on main at the first or second raise.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Titles that execute rdtsc themselves ignored APS5_TIME_SCALE, because
only sceKernelReadTsc scaled its ticks. The reported TSC frequency is
now divided by the scale and sceKernelReadTsc returns raw ticks, so
ticks over the reported frequency follow the scale for both. The
JobManager worker spin keeps real time through the host frequency.
AI-assisted: yes
The only conflict is sceKernelGettimezone in Time.cpp: main now reports the host zone, so the zone is built from that into a local and copied out with the checked write.
Both read their caller's structures in place before validation, so an unreadable pointer crashed the host. Copy them in with a checked read first and return EFAULT, as FreeBSD 11.4 does.
Fixes#11370
The only conflict is in Time.cpp with the guest timezone store of the
settimeofday change: gettimeofday reads the timezone under the mutex into
a local copy, releases it, then copies it out with the checked write.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A clock id with CPUCLOCK_PROCESS_BIT threw in clock_gettime and
clock_getres. FreeBSD 11 get_cputime reads the process's CPU time, or
fails with EINVAL when pget finds no such process, and kern_clock_getres
gives every CPU clock the CPU time resolution without a lookup. The
guest's own process clock now reads CLOCK_PROCESS_CPUTIME_ID, any other
fails with EINVAL, as kill already finds no other process.
FreeBSD 11.4's kern_settimeofday rejects a timeval with a microsecond
field outside 0 to 999999 or a negative second field with EINVAL before
changing anything, sets the clock from a valid timeval, and then stores
the timezone, which gettimeofday reports. settimeofday now checks the
timeval the same way and throws for a valid one, as clock_settime does,
because the host clock is not changed. A timezone given without a
timeval is stored and gettimeofday reports it instead of 0 and 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceKernelInternalMemoryGetAvailableSize with an output, mlockall with valid flags and clock_settime with a valid CLOCK_REALTIME request threw bare exceptions on every such call, so progress.py counted them as done. Internal pool statistics, locking every mapping and setting the clock are not implemented, so those calls now go through NotImplemented_nid_no_patch. The -1, EINVAL and EFAULT answers before them are unchanged.
These wrote their result through the caller's pointer in place, or read
the itimerval of setitimer without a check, so a pointer into unmapped
memory crashed the host. Build the result locally and copy it out with
the checked write (and read the setitimer value with a checked read),
failing with EFAULT as FreeBSD 11.4 does: copyout in sys_gettimeofday
(sys/kern/kern_time.c:646 and 651) and sys_getitimer (kern_time.c:745),
copyin (kern_time.c:803) and copyout (kern_time.c:808) in sys_setitimer.
getrlimit and getrusage are left to #8986 and #8988, which cover the same
family for those two calls.
Builds on fix/output-structs-efault, which adds GuestCopyOut_nid_no_patch.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
sceKernelGetTscFrequency times 20 ms of TSC ticks against the monotonic
clock. A thread that moves to another CPU during the sleep reads its end
value from another TSC. Where the TSCs of the CPUs differ, as on a Windows
VM whose vCPU 0 counts about 3.8 billion ticks ahead of the others, the
frequency came out as 0 or as 180 GHz. Pin the calibrating thread to the
CPU it runs on and restore its affinity afterwards.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Linux and macOS, sceKernelRaiseException runs the guest handler in
the target's SIGUSR1 handler wherever the thread was interrupted. A
thread interrupted while it held the address-wait lock of
sceKernelSyncOnAddressWait, sceKernelSyncOnAddressWake or _umtx_op ran
the handler with the lock held, so a handler that waits, as a
stop-the-world collector's does, blocked every other thread that waits
on or wakes an address. #5756 fixed this on Windows.
The address-wait lock is now taken under DeliveryDeferral on every
host. On POSIX the deferral is a count per thread: while it is raised
the SIGUSR1 handler only records the signal, and DeliverDeferred sends
it to the thread again once the lock is released. An address waiter
waits on its own semaphore (sem_t, a dispatch semaphore on macOS) with
the lock released and the deferral lifted, so a raise reaches a thread
that waits for an address without a timeout.
The address-lock phases of guest_raise_exception now run on every
host, with a new phase that raises at a thread in an unbounded address
wait.
sceKernelGettimezone wrote {0, 0} for every zone. A PS5 on firmware
13.40 set to US Eastern returned tz_minuteswest -300 and tz_dsttime 60:
the standard offset in minutes east of UTC, without the summer time in
effect on the run date, and the zone's daylight saving amount in
minutes. Both now come from the host zone, through the same HostZoneAt
and HostDaylightAmount the calendar conversions use.
guest_freebsd_clocks checks five TZ settings (UTC0, IST-5:30, BRT+3,
EST5EDT, CET-1CEST) and the null pointer.
The PS5 3.20 export lists have ktimer_* in libkernel as well as
libkernel_sys, and on Windows a libkernel import never reaches
libkernel_sys. The implementation moves unchanged to
libkernel/Time/Ktimer.cpp.
sceKernelGetProcessTimeCounter counted nanoseconds and
sceKernelGetProcessTimeCounterFrequency returned 1 GHz, while
sceKernelGetTscFrequency reports the host TSC rate. On the console the
process time counter runs at the TSC frequency, so a title may scale
the counter by sceKernelGetTscFrequency. PPSA34501 does, and its clock
ran 3.4 times slow on a 3.4 GHz host: it paced its 60 Hz frames to
17.6 fps and its game time and audio drifted apart.
The counter now ticks at sceKernelGetTscFrequency, over the same
monotonic clock and APS5_TIME_SCALE, and its frequency is that rate.
kern_clock_nanosleep limits the sleep to INT32_MAX / 2 seconds and sleeps
for that long (sys/kern/kern_time.c:543-547). The guest multiplied tv_sec
by 1e9 in 64 bits, so a request of 18446744074 seconds or more wrapped to a
short sleep: nanosleep with tv_sec = INT64_MAX returned after about 0.1 s.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A wake set after the previous wait returned left the per-thread
auto-reset event signalled, so the next short sleep returned from its
wait at once and spun to its deadline. With APS5_COARSE_TIMED_WAITS or
a request under 50 us the sleep now takes TimedWait::SleepNanos as on
main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>