handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes

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>
This commit is contained in:
MechaCat02
2026-06-05 07:19:08 +02:00
parent acd1656753
commit ef93a4fa14
620 changed files with 108303 additions and 1 deletions

View File

@@ -0,0 +1,123 @@
# 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`:
```cpp
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`:
```cpp
uint32_t NtClose(uint32_t handle) {
return kernel_state()->object_table()->ReleaseHandle(handle);
}
```
`xenia-canary/src/xenia/kernel/util/object_table.cc:189-208`:
```cpp
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_entry` body calls `xboxkrnl::NtClose(obj_handle)`.
* Returns `true` (=1) on `XSUCCESS`, `false` (=0) on `XFAILED` (after setting last error).
* Signature: `dword_result_t` — emitter records `ctx.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`:
```rust
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 (mirrors `XFAILED(X_STATUS_INVALID_HANDLE)` branch).
* Otherwise performs ref-counted release inline (decrement; drop on zero, scrub `async_file_handles` and `disarm_timer`) — identical body to `exports::nt_close` but written inline to avoid widening the exports API for a single XAM helper.
* Returns 1.
* 4 new unit tests:
* `xam_task_close_handle_valid_handle_returns_one_and_releases`
* `xam_task_close_handle_invalid_handle_returns_zero`
* `xam_task_close_handle_respects_refcount`
* `xam_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::SetLastError` is 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.