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>
124 lines
4.9 KiB
Markdown
124 lines
4.9 KiB
Markdown
# 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.
|