409. The pthread family, and a prediction kept
2026-09-04 - directed, while the Agc probe set is out for hardware
What was done
Implemented the pthread scheduling and identity family:
scePthreadGetschedparam (34 calls),
scePthreadSetschedparam, scePthreadSetprio,
scePthreadRename, scePthreadCondattrDestroy
and pthread_setcancelstate.
| before | after | |
|---|---|---|
| unimplemented functions called | 35 | 26 |
| calls landing on a stub | 914 | 32 |
ThreadRecord gained a scheduling policy and a
cancellation state beside the priority it already held - which is
precisely what D523 predicted would be needed, in as
many words: "orbistoun keeps no per-thread scheduling record ... the
moment something does, this needs the per-thread record rather than a
wider Ok." scePthreadGetschedparam is a
read-back and it is called 34 times, so the moment arrived.
The four-byte rule, somewhere unmissable
The guest passes policy at 0x…c86c and
param at 0x…c868 - four bytes
apart, because a sched_param is one
int and a caller putting both on its stack puts them
adjacent. An eight-byte write to either destroys the other silently.
D272 said this; here it is with no room to be careless in. Guarded with
a sentinel either side.
What was deliberately not implemented
scePthreadAttrGetstackaddr. Its arg0 and
arg1 in the run are byte-for-byte identical to
scePthreadAttrGet's, and arg0 is a
thread handle where an attribute object belongs. Nothing
explains that, and D527 is this project mistaking a register that
survived a tail-jump for an argument. One unexplained coincidence is not
evidence.
Guards
Seven written, six broken and caught: eight-byte writes to the
adjacent out-parameters; policy and priority written to swapped slots;
the priority read from the wrong register; setprio
resetting the policy; set_scheduling overwriting a policy
it was told to leave; and a get writing before checking the handle.
One break did not fire, and that is written into the
test. Removing the is_issued preamble from
pthread_rename does not fail the handle guard, because
thread::rename refuses unknown handles too - the property
survives on a second check downstream. A break removing both does fire.
It is recorded in the test because a guard that cannot say which of two
checks is holding it up would not notice one of them rotting, and the
two do not cover the same set: is_issued catches a handle
handed out whose record never landed, and nothing else would see
that.
The problem this exposes, which is not in the work
None of it moves a recorded metric.
standing is an integer percentage of calls reaching an
implementation, and 914 stubbed of 419,091 already rounded to 100%; so
does 32. Reach, imports and frames are unchanged, and the compat record
cannot see a 96% reduction in calls resting on placeholders. That is a
gap in what the record measures. Left as an observation rather than
folded into this change - a metric edited in the same commit as the work
it scores is a metric nobody can check.
Where it leaves the non-Agc surface
Nearly done. What is left outside Agc is
sceKernelUuidCreate (3 calls), _sigprocmask
(3), and a tail of eleven functions called once each -
three VideoOut, three libc, four service initialisers and one unnamed
PS5Util hash.