# Rust acceptor forced cancellation at callback boundaries

## Contract and reproduced defect

Decision 17 distinguishes a forced cancellation, which closes connections without
Logout, from an application Logout that waits for the exchange. An acceptor's
`run` previously read cancellation only between whole polling turns. During one
turn it could acquire a writer lease after the factory cancelled, finish recovery
and answer Logon after a store hook cancelled, admit the rest of the backlog, or
deliver another buffered application frame after its callback cancelled.

The baseline is `e84d207b4959e304942b9e7bb59b4fd3ff5e73f4`; the qualified runtime
candidate is `ff5a7d60ef9f9e76d8175903231ac8b271f07904`. The identical eight-case
fixture has SHA256
`b25b00632ed9dc424649fcd9425ce523e9120006fdd572afdb82975fe49e97ff`.
On BEELINK2, both baseline editions passed three controls and failed the same
five cancellation cases. The initial baseline run's unused-import warning was
removed before both authoritative baseline runs; it was not a runtime failure.

## Change and limits

The cancellation flag is borrowed through private acceptor routines only during
`run`. Checks precede backlog admission and surround factory, writer acquisition,
recovery, session polling and application delivery. Cancellation in the factory
prevents writer acquisition; cancellation after acquisition closes/checkpoints
under the lease and releases it once before registering a session. The existing
owning connection guard also closes clones retained by the factory. A stopped
admission is not counted as an accepted/closed session or a setup/TLS failure.
An explicit factory error keeps its refusal diagnostic and counter.

Cancellation from delivery returns before consuming/advancing to the next frame
or stepping the next session. Run exit uses the existing forced close for admitted
sessions and pending TLS, with no Logout. `poll` carries no cancellation flag,
including after a cancelled run. The API, validation policy and durable formats
are unchanged. Cancellation cannot preempt a callback or `Session::step` already
executing: that work must return to the owner first. No callback preemption or
new physical transport qualification is claimed.

## Qualification

Scan held an exclusive `isol-no-shared` CPU lease for qualification. Builds used
`-j2`, one test thread, no dev/test debug information, and the session's absolute
`LIBHFT_BUILD_ROOT`. The memory guard required 64 GiB available to start and
would stop this job below 32 GiB or on a foreign compiler/test process. Test
Cargo/Rust were 1.92.0; strict Clippy used the retained 1.93.1 toolchain.

| Gate | Default | Open |
|---|---:|---:|
| Identical baseline regression fixture | 3 passed, 5 failed | 3 passed, 5 failed |
| Candidate regression fixture | 8 passed | 8 passed |
| Combined cancellation/Logout/startup/shutdown focus | 68 passed | 65 passed |
| Complete Rust stage | 2,212 passed | 2,020 passed |
| Strict all-target Clippy, `-D warnings` | passed | passed |

Each full stage had zero failures and zero skipped stages, with the same one
existing ignored builders doctest. The conformance wrapper returns 2/PARTIAL
because only one of 362 monorepo stages was selected per invocation: this
qualifies the two Rust stages, not the whole monorepo suite. The exact commands,
stdout, stderr, reports and toolchain versions are retained. Before/after source
proofs match all 377 Rust and 632 shared path/SHA256 pairs.

HP's initial exclusive queue was cancelled before its test script ran. BEELINK2
baseline runs were serial and guarded; its candidate compile was interrupted
when a foreign Java build started and was not counted as qualification. An idle
Roslyn server was distinguished by unchanged CPU samples and RSS below 2 GiB;
any activity or a new build still stopped this job. Scan supplies all final
candidate focus, full-stage and Clippy evidence.

Cold checks passed: API inventory matches, its 13 unit tests pass, coverage matrix
matches, scoped fixture rustfmt, diff check, and export scan of the two changed
engine files (zero findings). Existing compact source formatting was preserved.

## Retained evidence and parity status

The packet is `~/archive/libhft/rust-acceptor-cancellation-20261013` on BEELINK2,
HP and Scan. It retains baseline/focus/full-stage/Clippy outputs, actual stage
commands and reports, source path/hash maps, source bundle and checksums. Runtime
and shared inputs are compared again after rebase before publishing to main.

This closes an acceptor lifecycle defect without changing static parity scores:
82/82 applicable APIs, 27/27 protocol rows, 43 complete subsystems, two partial
and one not applicable. Validation-mode alignment is separately owned. Live
production endpoint/venue and FIXP certification remain external partials.
