|
| 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. |
0 commit comments