Skip to content

Commit e8f6536

Browse files
committed
Record isolated kqueue deletion-flush evidence for #83
Preserve complete red and green macOS job logs, checkout provenance, dependency source hashes and source-based attribution. Archive read-back comparisons passed. Independent review accepted attribution with bounded scope; parent corrected its inverted worker race condition and recovered the complete baseline checkout log. One green run is not determinism; write skips, dependency pin, separate EBADF probe and issue status remain unchanged. Parent just lint and just ci passed.
1 parent ce434db commit e8f6536

5 files changed

Lines changed: 107 additions & 17 deletions

File tree

‎PRODUCTION_READINESS.md‎

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -925,9 +925,11 @@ backend, and single-backend coverage is not backend coverage.
925925

926926
**Gaps (tracked under #76's successors):** in-flight cancellation is now covered
927927
for both reads and writes, abortive and orderly, on io_uring and epoll (#83).
928-
kqueue remains the unproven island: the abortive read test stalls there for
929-
reasons still unestablished, and both write variants share the same
930-
cancel-then-close shape and are skipped for the same reason. The epoll fix works
928+
kqueue cancellation remains unproven in the shipped dependency: the abortive
929+
read stall is attributed to libxev's dropped `EV_DELETE` during `tick(0)` by the
930+
[isolated deletion-flush experiment](docs/research/XEV_KQUEUE_83/20260913-flush/README.md).
931+
The fix is not adopted. Both write variants remain skipped and were not
932+
validated by that experiment; #83 stays open. The epoll fix works
931933
around what looks like an upstream libxev defect: its epoll TCP watcher
932934
duplicates the fd per operation and the normal completion path closes that
933935
duplicate, but the cancellation path (`stop_completion`) only does
@@ -970,7 +972,14 @@ after half a record would be un-authenticatable garbage.
970972
The abortive in-flight-read test stalls on macOS/kqueue: after the server's
971973
cancellation completes, the client's plain socket close is armed
972974
(`phase = .released`) but its callback never fires and `Loop.active == 0`.
973-
Unresolved, and now bounded by evidence rather than by hypothesis.
975+
CI run `34742256947` reproduces this exact state at diagnostic revision
976+
`f3d0a0e`; run `34742815211` passes the same enabled test at `7715eb5` with only
977+
upstream mitchellh/libxev#224's deletion-flush hunk applied to the pinned source.
978+
The tick-local deletion is otherwise discarded on `wait == 0`. A stale EOF
979+
can re-fire and decrement `active` again, preventing the loop from reaching
980+
thread-pool completion migration. Source analysis and the isolated red/green
981+
experiment establish the abortive-read attribution, not deterministic behavior
982+
across all interleavings. The production pin and kqueue skips are unchanged.
974983

975984
Two hypotheses were tested against real macOS runs and both are dead: libxev
976985
mis-accounting `active` on the cancel path (a single-socket probe comes back
@@ -986,10 +995,10 @@ other has a read armed:
986995
src/backend/kqueue.zig:1285 in perform (xev_posix.close(op.fd))
987996
src/backend/kqueue.zig:960 in thread_perform
988997

989-
That is a double close of an already-closed fd in the thread-pool worker.
990-
Whether it shares a root cause with the `Conn` stall is unestablished: the ztls
991-
test stalls rather than panicking, so they may be separate defects in the same
992-
area. Upstream-reportable either way.
998+
The thread-pool worker attempted to close an invalid fd. The earlier closer or
999+
possible duplicate scheduling remains unestablished. The deletion-flush
1000+
experiment did not run this probe and does not establish its relationship to
1001+
the `Conn` stall.
9931002

9941003
**macOS is now CI-gated.** A `macos-15` job runs `just integrations-ci` on kqueue,
9951004
scoped to the 0.16 integrations rather than the whole lane (conformance needs a
@@ -1000,15 +1009,14 @@ manual run — the 21/21 that closed #76 was stale within a day.
10001009

10011010
The runner immediately narrowed #83, which is the argument for having it. Only
10021011
the **abortive** close with a read in flight stalls on kqueue; the **orderly**
1003-
close passes there and runs normally. The two differ by exactly one thing — the
1004-
close_notify write between the cancel and the socket close — so an extra
1005-
completion cycle at that point is enough to unstick it. That is the sharpest clue
1006-
available and was invisible from a single manual run, which had only ever
1007-
exercised the abortive case.
1008-
1009-
The abortive case is skipped on kqueue rather than asserted-as-failing: a stalled
1010-
loop's teardown crashes, so the failure cannot be caught and asserted. In-flight
1011-
write cancellation remains separately unproven.
1012+
close passes there and runs normally. The extra close_notify write changes the
1013+
event path, but timing alone was not accepted as proof of a repair. The isolated
1014+
deletion-flush experiment preserves the abortive test's trigger and timing.
1015+
1016+
The abortive case remains skipped on kqueue until a dependency fix is adopted.
1017+
Earlier stalled-loop teardowns crashed; the preserved CI baseline instead
1018+
returns `LoopStalled` cleanly. In-flight write cancellation remains separately
1019+
unproven.
10121020

10131021

10141022

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Kqueue deletion-flush experiment (#83)
2+
3+
Project status and remaining scope live in
4+
[PRODUCTION_READINESS.md](../../../../PRODUCTION_READINESS.md).
5+
This directory preserves one baseline and one candidate macOS CI job log.
6+
`SHA256SUMS` covers the losslessly compressed logs; both decompressed files were
7+
compared byte-for-byte with the original downloads.
8+
9+
## Provenance
10+
11+
Both jobs use `macos-15`, Zig 0.16.0, and `just integrations-ci`.
12+
The common main revision is `ce434db9b29a1fc51034de66241d9ed0c2e4f6b0`.
13+
GitHub tests synthetic merge commits; each complete job log includes its
14+
checkout line, not merely the test output.
15+
16+
- Baseline: [run 34742256947](https://github.com/mattrobenolt/ztls/actions/runs/34742256947),
17+
job `103683823687`, branch revision
18+
`f3d0a0e67e1c8027a5b382be44da003453c887af`, merge checkout `5583296`.
19+
- Candidate: [run 34742815211](https://github.com/mattrobenolt/ztls/actions/runs/34742815211),
20+
job `103685296932`, branch revision
21+
`7715eb57c9f81711ccf687eabc8cee759624773a`, merge checkout `85bd368`.
22+
- Diagnostic branch: [PR #97](https://github.com/mattrobenolt/ztls/pull/97).
23+
The baseline changes one line: it enables the existing abortive-read test on
24+
kqueue. The candidate adds a macOS-only preparation step and patch files.
25+
Neither changes the test timing, assertions, callbacks, or write skips.
26+
- libxev pin: `9ce8e8e6ff89e583258a7f8e7adeeeaeae8611bf`, package
27+
`libxev-0.0.0-86vtcwIRFADbH4hk-EjROXxlrKIRPQdA41XiTSytYO-F`.
28+
The candidate modifies only its checkout-local `src/backend/kqueue.zig`:
29+
30+
```diff
31+
- if (wait == 0) break;
32+
+ if (wait == 0 and changes == 0) break;
33+
```
34+
35+
This is the deletion-flush hunk from
36+
[mitchellh/libxev#224](https://github.com/mitchellh/libxev/pull/224),
37+
inspected at `7497c85dfc3ae5141308944f2cd47c836772c300`.
38+
No other hunk is applied. The script verifies the pristine file hash before
39+
applying the patch, then prints the changed hash before the integration gate:
40+
41+
- Before: `01c0c18f47f81b718f03c4d15279c88852ad148a423fa08d5b3f128cfd9db069`
42+
- After: `de8b5fcdec908f9e0766bb711ad26d81dce18d7deaca26d8d84740e7ada948bc`
43+
44+
The cache mutation is an isolated diagnostic technique, not a production
45+
installation method. No dependency override or CI patch is adopted on main.
46+
47+
## Observations
48+
49+
The baseline's enabled abortive-read test returns `error.LoopStalled`:
50+
51+
```text
52+
stalled on kqueue: srv=.{ .state = .closed, .wait = .idle, .phase = .released }
53+
cli=closing/idle/released active=0 events={ .read_canceled, .closed }
54+
```
55+
56+
The candidate executes that same test and reports `PASS`. The orderly-read test
57+
also passes in both logs. The two write-test `PASS` lines are empty conditional
58+
bodies on kqueue: they are not evidence of write cancellation coverage.
59+
60+
## Source analysis and limits
61+
62+
In pinned libxev `Loop.tick`, `changes` and the deletion buffer are local.
63+
The kevent event path stages a deletion after a `.disarm` callback, then the
64+
unconditional `wait == 0` break discards it. A level-triggered EOF can therefore
65+
re-fire for the retired read. Unlike the completions-queue path, the event path
66+
unconditionally decrements `active` after `.disarm`.
67+
68+
The client read callback requests its thread-pool socket close. If the stale
69+
read event is reaped before the worker closes the fd, that extra decrement can
70+
consume the close operation's active count. At zero, the loop gate excludes
71+
entry to the thread-pool completion migration, so the close callback remains
72+
undelivered. This explains the observed state without a missing cancel.
73+
The changed condition keeps the tick alive to submit its staged deletion.
74+
75+
The source analysis and isolated red/green experiment support attribution of
76+
this abortive-read stall to the dropped deletion. The logs are not per-event
77+
traces; they do not directly record each intermediate callback or worker
78+
interleaving. One green run does not establish deterministic behavior or validate
79+
all upstream PR hunks. The separate `two_conn_cancel` EBADF probe was not run
80+
in this experiment, and its relationship to this stall remains unestablished.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
5fd1e64cd53e5c147032f7473d7b73801720878e9c450e5b67bbff8c4d7dbd5b baseline-34742256947.log.xz
2+
24933169829b0d2e6f29a0bf7d9a36a767abbd5d176423c90ab297ec5a47bb66 candidate-34742815211.log.xz
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)