Skip to main content
Print

ThreadMan and Scheduler Model for Runtime Authors

Type: REFERENCE   Status: MAINTAINED   Scope: PSP thread creation, start semantics, lifecycle, registers, waits, callbacks, and scheduler-visible state

ThreadMan is a semantic subsystem, not a thin wrapper around host OS threads. A useful runtime model must explain guest-visible identity, priority, lifecycle, wait ownership, timeout, wakeup, callback-aware waits, argument placement, initial machine state, and the distinction between a route progressing and a PSP contract being complete. This page is title-neutral; project-specific source ownership is summarized in the Nakagawa Wiki threading page.

Evidence boundary

Every substantive statement belongs to an evidence class:

  • HARDWARE_MEASURED: observed on a real PSP within a stated, bounded scope.
  • PUBLIC_HEADER_FACT: declared by a public SDK/header or generated public reference.
  • OPEN_FIRMWARE_IMPLEMENTATION: visible in an open emulator or firmware-like implementation.
  • EMULATOR_CONSENSUS: PPSSPP and JPCSP model the same behavior; this remains comparator evidence.
  • IMPLEMENTATION_DISAGREEMENT: public models differ.
  • SOURCE_VERIFIED_NAKAGAWA: confirmed in the reviewed Nakagawa source tree.
  • INFERENCE: a reasoned interpretation that is not directly measured or declared.
  • HARDWARE_UNKNOWN: available evidence does not establish real-PSP behavior.

A local test, successful route, or emulator match is not hardware validation. The CreateThread and StartThread hardware-oracle specifications described here are planned and not run.

How We Know

ABI and CreateThread

For the measured PSP user-call ABI, arguments 5 through 8 arrive in $t0 through $t3: HARDWARE_MEASURED within that user-call scope. A real-firmware sceKernelCreateThread measurement established its fifth argument in $t0; arguments 6 through 8 were not separately measured in that run. Nakagawa’s stack_arg() implements this extraction: SOURCE_VERIFIED_NAKAGAWA.

The public header declares these attributes:

Attribute Value Evidence and remaining question
PSP_THREAD_ATTR_VFPU 0x00004000 PUBLIC_HEADER_FACT; hardware context consequences are unmeasured here.
PSP_THREAD_ATTR_USER 0x80000000 PUBLIC_HEADER_FACT; legal mask and normalization remain unknown.
PSP_THREAD_ATTR_USBWLAN 0xa0000000 PUBLIC_HEADER_FACT; acceptance and normalization remain unknown.
PSP_THREAD_ATTR_VSH 0xc0000000 PUBLIC_HEADER_FACT; acceptance and normalization remain unknown.
PSP_THREAD_ATTR_SCRATCH_SRAM 0x00008000 PUBLIC_HEADER_FACT; legality for the tested caller remains unknown.
PSP_THREAD_ATTR_NO_FILLSTACK 0x00100000 PUBLIC_HEADER_FACT; requires a bounded in-allocation observation.
PSP_THREAD_ATTR_CLEAR_STACK 0x00200000 PUBLIC_HEADER_FACT; pattern and precedence require measurement.

PPSSPP and JPCSP additionally model KERNEL=0x00001000 and LOW_STACK=0x00400000: EMULATOR_CONSENSUS, not public-header facts. Legal user masks, KERNEL from a user caller, SCRATCH_SRAM legality, USBWLAN/VSH normalization, unknown bits, and priority bounds remain IMPLEMENTATION_DISAGREEMENT or HARDWARE_UNKNOWN.

Option and stack identity

The public option type contains SceSize size and SceUID stackMpid. JPCSP treats stackMpid as a partition identifier for allocation; PPSSPP notices the field but gives it no allocation effect. The PSPSDK block-UID wording carries an uncertainty marker, so a genuine block UID remains only a competing hypothesis. The final firmware meaning is HARDWARE_UNKNOWN.

PPSSPP and JPCSP expose SceKernelThreadInfo.stack as a low allocation/base address: EMULATOR_CONSENSUS, not confirmed PSP semantics. Establish address orientation before taking any bounded in-stack sample. Do not probe an adjacent byte or read outside the allocation.

StartThread argument and initial state

PPSSPP and JPCSP agree that a nonzero argument block is copied to the child stack, with the argument byte count in $a0 and the child-stack copy address in $a1: EMULATOR_CONSENSUS. They disagree for a non-NULL pointer with argSize=0: PPSSPP models $a0=0, $a1=0, while JPCSP models $a0=0 with $a1 holding a child-stack address. That discriminator is IMPLEMENTATION_DISAGREEMENT and HARDWARE_UNKNOWN.

Both models reserve a modeled kernel area, align an argument area, and use a modeled 0x40-byte frame. Exact PSP spacing is HARDWARE_UNKNOWN. Initial GP differs: PPSSPP derives module metadata, while JPCSP uses the creator GP. Initial RA differs: PPSSPP uses a stack return stub, while JPCSP uses an internal HLE exit handler. Cross-module GP and initial RA are high-value hardware discriminators. 0xDEADBEEF and 0x7F800001 are emulator initialization/debug sentinels, not hardware initial values.

Lifecycle, waits, callbacks, and scheduling

The models cover a dormant thread starting, an active-thread start error, restarting an exited thread, deleting a never-started dormant thread, waiting on a never-started dormant thread, and rejecting an invalid ID. Exact PSP statuses and lifecycle ordering remain HARDWARE_UNKNOWN until measured.

Both models reschedule immediately when a started child has a strictly better priority. Equal-priority and worse-priority ordering differs, and physical ordering remains unknown. A source-owned oracle should mark BEFORE_START, CHILD_ENTRY, and AFTER_START with one monotonic global counter. It should not add sleeps, arbitrary delays, priority changes, or hardcoded timing constants.

An ordinary sceKernelWaitEventFlag wait does not dispatch a callback; a subsequent sceKernelCheckCallback() does: HARDWARE_MEASURED for that sequence. At a measured non-delete thread-exit seam, positive 0x77 exits as 0x77, while error-shaped 0x800201ac exits as 0x800200d2: HARDWARE_MEASURED for that exact seam only. Neither observation defines every callback entry or thread-exit path.

Planned hardware-oracle work

The CreateThread specification marker is CREATE_THREAD_HARDWARE_ORACLE_SPEC_READY followed by HARDWARE_NOT_RUN. Its minimum plan has at least 16 records: 13 discriminators and 3 controls, with at least 2 launches selected from lower-risk phases. Lower-risk is only a planning label; no probe is guaranteed harmless. The first option cells are NULL, size=4, size=8 with USER partition 2, invalid partition 7, size=8 with KERNEL partition 1, and a genuine block UID as a competing hypothesis. The plan also covers declared attributes, an unknown-bit mutation, bounded priority edges, stack-size controls, and malformed options.

The StartThread specification marker is START_THREAD_HARDWARE_ORACLE_SPEC_READY followed by HARDWARE_NOT_RUN. Its minimum plan has 17 records, including at least 3 lower-risk launches as a planning subset. It covers no-argument and copied-argument starts, the zero-size/non-NULL disagreement, NULL and misaligned pointers, initial SP/GP/RA and raw registers, starting twice, restarting an exited thread, delete/wait before start, invalid IDs, returns of 0/positive/negative values, and priority markers. Huge sizes and malformed pointers are deferred until bounded normal cases establish the observation path.

All records must preserve raw return values and distinguish a returned UID from a rejected call. The stack orientation must be established before in-stack sampling. LOW_STACK requires simultaneous paired allocations rather than sequential create/delete address reuse. NO_FILLSTACK requires a bounded in-stack sample or count/checksum over a defined region, not a single byte. No planned record is evidence until an authorized real-device run produces it.

Do Not Infer

  • Host OS scheduling is not PSP priority or wait semantics.
  • PPSSPP/JPCSP consensus is not hardware proof.
  • A route that advances is not proof of correct callback, timeout, wake, register, or lifecycle semantics.
  • A current Nakagawa implementation detail is not universal PSP behavior.
  • A block UID is not the established meaning of stackMpid.
  • An adjacent/outside-allocation probe does not establish stack orientation.
  • Planned or lower-risk oracle cases are not evidence, and no probe is guaranteed harmless.
  • Private provenance paths, retail inputs, traces, and run metadata are not part of this public page.

Open questions

High-value source-owned matrices include attribute legality and normalization, option allocation semantics, stack orientation, initial SP/GP/RA, zero-size argument handling, lifecycle raw codes, equal/worse-priority ordering, callback arrival during waits, and hardware attribute consequences. Keep unknown cells explicit in the Open Research Questions registry.

Primary sources

Table of Contents