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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
On Linux, APS5_TRACE_WAITS and APS5_TRACE_USLEEP used an image base and a thread
id of 0, so "exe+0x..." printed absolute addresses and every site reported
"tid 0". The call site is now resolved with dladdr1(RTLD_DL_LINKMAP) and printed
relative to its module's load address ("exe+..." for the executable, the module
file name otherwise), matching guest offsets in a disassembly; the thread id is
gettid(). Descriptions are cached per call site, so polling titles do not pay a
dladdr1 per wait. Windows output is unchanged.
clock_gettime threw invalid_argument when the result pointer was null,
before looking at the clock id. FreeBSD 11.4's sys_clock_gettime reads the
clock through kern_clock_gettime, which fails with EINVAL for an unknown
clock, and only then copies the result out, so a null pointer fails with
EFAULT.
Read the clock into a local value first, then fail with -1 and EFAULT when
the pointer is null. A clock that is not supported still throws before the
pointer is looked at. Test the null pointer for the supported clocks.
Written with AI assistance (Claude).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A sleep of 1 ms or less returns once the AGC driver sees a GPU batch
complete (a thread waits on the recorder's timeline semaphore), runs a
batch's completion actions, or VideoOut completes a flip. A thread
returns early at most 8 times in a run of back-to-back sleeps; the run
restarts when the thread submits AGC work or did not sleep for the
requested time. APS5_NO_SLEEP_WAKE=1 keeps every sleep in full.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PS5MC (PPSA99420, Kodi 22) imports clock_settime (d7nUj1LOdDU) from
libkernel.prx, one reference, after sigwaitinfo in its import order.
Predicted stop once the imports ahead of it are covered (not a run):
FAIL: unresolved ELF import d7nUj1LOdDU from libkernel.prx
The NID is computed with tools/nid_names.py and matches aerolib.
Follows FreeBSD 11.4 sys/kern/kern_time.c sys_clock_settime and
kern_clock_settime (8c639945a44588f6cfdddfb057bb5aad88735da0) for a
root caller: EFAULT for a null time (copyin), EINVAL for any clock but
CLOCK_REALTIME and for a negative tv_sec or a tv_nsec outside 0 to
999999999. A valid CLOCK_REALTIME request throws with the requested
time named, since the host clock must not be set and no guest clock
offset is kept.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: David <david@example.com>
sceKernelConvertUtcToLocaltime and sceKernelConvertLocaltimeToUtc returned
the time unchanged while libc localtime follows the host zone, so a title
that mixes them shows UTC as local time (#5067).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iLxaSz4qjmirfqKZdhVQk
A thread CPU clock whose thread has finished made clock_gettime fail with errno EINVAL, but the sce wrappers passed the raw -1 through to the title. They now return the encoded errno like the other sce kernel calls.
FreeBSD's kern_clock_nanosleep writes the remaining time only when the sleep is cut short. A completed sleep zeroed rmtp, so nanosleep(&ts, &ts) in a polling loop slept zero time from the second call on.
sceKernelGetProcessTime and sceKernelGetProcessTimeCounter measured from
a start captured on their first call, so the first reading was about 0
however long the process had run, and exactly 0 when both reads fell in
one tick of the host clock (100 ns on Windows). Capture the start when
libkernel loads.
scePthreadCancel and pthread_cancel threw, scePthreadTestcancel did
nothing and the cleanup handler exports did not exist.
A cancel request is now recorded on the thread and acted on at
scePthreadTestcancel, the condition variable waits, scePthreadSem and
sem waits, scePthreadJoin and the sleeps, and under their POSIX names.
A thread blocked in one of the waits is woken; a condition wait takes
its mutex back before the thread exits. The thread exits with
PTHREAD_CANCELED through scePthreadExit, which now runs the handlers
of __pthread_cleanup_push_imp and __pthread_cleanup_pop_imp.
scePthreadSetcancelstate and scePthreadSetcanceltype reject unknown
values, and pthread_setcanceltype is added.
Constants, the handler node layout and the order of events follow
FreeBSD libthr. A sleep is not interrupted, file and socket calls are
not cancellation points and the asynchronous type acts like the
deferred one except at the two set calls (TechnicalDebt). New test:
guest_pthread_cancel.
FreeBSD's gettimeofday stores the time only when tp is not null, and
clock_getres the resolution only when res is not null; both succeed
otherwise. Both threw, which aborted a title probing a clock or only
asking for the time zone. Write into a local result instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SleepUntil, PollSleepUntil and SleepNanosCoarse each wait on one
thread_local high-resolution timer. A sceKernelRaiseException handler
that runs as an APC inside a sleeping thread's wait and sleeps itself
re-armed that timer and consumed its signal, so the interrupted sleep
never returned. A nested TimerWait now waits on its own timer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceKernelGetProcessTime and sceKernelGetProcessTimeCounter subtracted
GetStartNanos() from GetMonotonicNanos() in one expression. GCC reads the
clock first, so on the first call in a process the start was captured
after it and the result wrapped to about 2^64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A thread had one TimedWait waiter for every wait. When
sceKernelRaiseException reached a thread blocked in a TimedWait
condition, its handler ran as an APC inside that wait, and a wait in
the handler queued the same waiter on a second condition while it was
still linked into the first. The links of the first queue were
overwritten, so waiters behind it were never woken, and a wake of the
outer wait set the shared event and was consumed by the inner wait.
A wait now takes the thread's first waiter that is not in use, and
creates a deeper one for a wait nested inside another.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
A raise that suspends the thread after its wait returned but before the
wait count drops queued an APC that only ran at the next alertable wait.
Pending APCs are now run right after the count drops. The 0 ms poll in
Condition::waitSignalUntil runs under queueLock and is no longer
alertable, and a target that exits during the raise returns ESRCH.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The APC delivery was allocated after SuspendThread, so a target holding
the allocator lock deadlocked the raising thread; it is now allocated
before the suspend. The wait count a raise checks lived in the target's
thread_local storage and dangled once the thread exited; it is now a
member of PthreadPrivate, which TimedWait counts into through a
thread-local pointer bound when the thread starts, and a raise on a
thread that already finished returns ESRCH. TechnicalDebt gets one line
per unverified choice: Windows only, the FreeBSD ucontext layout, the
signal mask, the YMM upper halves, and delivery to threads outside
TimedWait.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run the SIGUSR1 handler recorded by sceKernelInstallExceptionHandler on the
target thread with a PS5 ucontext (uc_mcontext at 0x40, FreeBSD amd64
layout) of its interrupted state:
- on the calling thread, directly;
- on a thread in a libkernel wait, from an APC: the TimedWait waits are
alertable and count themselves in a per-thread wait state, so the
handler runs inside the wait, which then continues;
- on a running thread, by suspending it and redirecting it to an entry
that calls the handler and restores the possibly modified context.
Other signals return EINVAL, as in shadPS4; raising without a handler
throws. Linux still throws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clock_gettime and clock_getres served three FreeBSD clock ids with the wrong
clock or not at all:
- CLOCK_VIRTUAL (1) was the monotonic clock on Windows and threw on Linux; it
is the user CPU time of the process.
- CLOCK_PROF (2) threw on both hosts; it is the user plus system CPU time of
the process.
- CLOCK_SECOND (13) returned the coarse realtime clock with nanoseconds and a
sub-second resolution; it is the current second with tv_nsec 0 and a
resolution of one second.
Behaviour follows FreeBSD 11 clock_gettime(2) and kern_clock_gettime /
kern_clock_getres in sys/kern/kern_time.c.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nanosleep and _nanosleep forwarded the result of sceKernelNanosleep, which
returned -1 for a rejected request: errno was never set, and the sce call
returned a value that is not an sce error. A request with negative seconds
was rejected as well, where FreeBSD's kern_nanosleep returns 0 without
sleeping.
nanosleep and _nanosleep now return -1 with errno set to EINVAL when tv_nsec
is outside [0, 1000000000) and to EFAULT for a null request;
sceKernelNanosleep returns the matching SCE_KERNEL_ERROR code. A request
with valid nanoseconds and negative seconds returns 0 at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>