Source changes (dormant parity infra, retained from iterate 2.AI/2.AO): - xenia-kernel/exports.rs: nt_create_event manual_reset polarity + related event wiring - xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps (.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as regenerable local artifacts — see memory + HANDOFF for the running findings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.9 KiB
Phase C+7 — XamTaskCloseHandle (idx=102158)
Step 1 — Framing verification (reading-error #28 discipline)
Canary source (authoritative)
xenia-canary/src/xenia/kernel/xam/xam_task.cc:83-93:
dword_result_t XamTaskCloseHandle_entry(dword_t obj_handle) {
const X_STATUS error_code = xboxkrnl::NtClose(obj_handle);
if (XFAILED(error_code)) {
const uint32_t result = xboxkrnl::xeRtlNtStatusToDosError(error_code);
XThread::SetLastError(result);
return false;
}
return true;
}
DECLARE_XAM_EXPORT1(XamTaskCloseHandle, kNone, kImplemented);
xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc:414-416:
uint32_t NtClose(uint32_t handle) {
return kernel_state()->object_table()->ReleaseHandle(handle);
}
xenia-canary/src/xenia/kernel/util/object_table.cc:189-208:
X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) { /* lock */ return ReleaseHandleInLock(handle); }
X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) {
ObjectTableEntry* entry = LookupTableInLock(handle);
if (!entry) {
return X_STATUS_INVALID_HANDLE;
}
if (--entry->handle_ref_count == 0) {
return RemoveHandle(handle);
}
return X_STATUS_SUCCESS;
}
Framing CONFIRMED. The C+6½ XAM audit was accurate this time:
XamTaskCloseHandle_entrybody callsxboxkrnl::NtClose(obj_handle).- Returns
true(=1) onXSUCCESS,false(=0) onXFAILED(after setting last error). - Signature:
dword_result_t— emitter recordsctx.gpr[3]as the dword return.
Reading-error #28 lesson holds — and this time, the framing was right. The check itself is the discipline.
Ours source (pre-fix)
xenia-rs/crates/xenia-kernel/src/xam.rs:33:
state.register_export(Xam, 0x01B1, "XamTaskCloseHandle", stub_success);
stub_success (line 103) sets ctx.gpr[3] = 0. That's the divergence.
Phase A event context (idx 102156-102159 main chain)
canary tid=6 [102156] import.call XamTaskCloseHandle (ord 433)
canary tid=6 [102157] kernel.call XamTaskCloseHandle args={} args_resolved={}
canary tid=6 [102158] kernel.return XamTaskCloseHandle return_value=1
canary tid=6 [102159] import.call KeWaitForSingleObject
ours tid=1 [102156] import.call XamTaskCloseHandle (ord 433)
ours tid=1 [102157] kernel.call XamTaskCloseHandle args={} args_resolved={}
ours tid=1 [102158] kernel.return XamTaskCloseHandle return_value=0 ← DIVERGENT
ours tid=1 [102159] import.call KeWaitForSingleObject
args={} in both engines — the kernel.call args record is empty for XAM,
so the handle being closed is not directly visible in the JSONL. Per
canary's XamTaskSchedule_entry, the handle was returned via the
out-pointer from the preceding XamTaskSchedule at idx 102154 (the
12345 placeholder per xam_task.cc:48).
Step 3 — ours NtClose support for the handle
Ours's xam_task_schedule (xam.rs:216-288) mints a real Thread handle
via state.alloc_handle_for(KernelObject::Thread { … }). The handle is
a regular kernel object that ours's nt_close (exports.rs:2193-2225)
release-counts identically to canary's ObjectTable::ReleaseHandle.
No new infrastructure needed.
Step 4 — Fix shape
xenia-rs/crates/xenia-kernel/src/xam.rs:
- Line 33:
register_export(... stub_success)→register_export(... xam_task_close_handle) - New function
xam_task_close_handle(~38 LOC) that:- Reads handle from
ctx.gpr[3]. - Checks
state.objects.contains_key(&handle)— if absent, returns 0 (mirrorsXFAILED(X_STATUS_INVALID_HANDLE)branch). - Otherwise performs ref-counted release inline (decrement; drop on zero, scrub
async_file_handlesanddisarm_timer) — identical body toexports::nt_closebut written inline to avoid widening the exports API for a single XAM helper. - Returns 1.
- Reads handle from
- 4 new unit tests:
xam_task_close_handle_valid_handle_returns_one_and_releasesxam_task_close_handle_invalid_handle_returns_zeroxam_task_close_handle_respects_refcountxam_task_schedule_then_close_round_trip_returns_one
Total: +158 LOC, 1 file (38 body + 120 tests).
Step 5 — Outcome
- Main chain (tid=6 → tid=1): matched-prefix 102158 → 102164 (+6).
- New first divergence (next target): idx 102164 —
KeResetEvent return_value=1 vs 0(same class as the C+7 predecessor's KeSetEvent fix; C+7 memo flagged this as deferred). - All other chains unchanged (no regressions).
Discipline notes
- Reading-error #28 verification step took ~3 minutes (read canary source, confirm framing). Worth doing every single time.
- Did not need to escalate. Fix scope was within the predicted ~10 LOC body + tests envelope; no handle-table refactor required.
- Last-error semantics on the failure branch are intentionally not modeled —
XThread::SetLastErroris observation-only in this Phase A horizon (canary's emitter records dword return, not last-error). Note added in the code comment for future investigators.