# FIXP acceptor lifecycle and recovery review — 2026-10-11

Reviewed from `3cba4197e625209b360ae7aa2192a7070573254b` in the isolated
`codex-review-acceptor-1011` worktree. Three agents reviewed C++, Java and Rust
in parallel. Root owned repository edits and serialized heavy jobs through
`tools/host-cpu-slot`, with at most 16 build jobs and native LTO capped at one. Final integration
runs on `hp`, with four C++ jobs and one Cargo job.

## Defects and fixes

* **C++ failed replay reset:** changing the negotiated session ID can fail while
  resetting its replay store. The runner previously retained its listener and
  could admit another child using a faulted or unmapped store. It now retires
  the child and listener before releasing store ownership. Serving resumes
  through `Listen`, including normal reopen and HA primary arbitration.
* **C++ and Rust listener restart:** an explicit Close/listen/accept cycle retained
  the old connection's finalization arithmetic. A Terminate before the new
  connection established could retire the new session ID using the old
  acknowledgment, causing later Establish requests to be rejected. Close now
  clears the connection handshake while retaining finalized IDs, outbound
  sequence state and replay history.
* **Java exhausted admission:** recovered sequence exhaustion can return
  CONFIG_ERROR after a child is admitted and marked rejected. The TCP single
  owner and direct multi owner previously retained the rejected child, preventing
  reaccept or consuming a peer slot indefinitely. The TCP owner now closes
  the rejected child, and the direct multi owner releases the rejected slot
  after existing callback identity/generation checks. Its TCP wrapper closes
  the corresponding owned transport. Healthy owners survive preflight
  CONFIG_ERROR, and sibling sessions remain usable.
* **Java recovery-request clock:** an automatic RetransmitRequest sent
  in response to a high Sequence did not refresh the outbound silence deadline. A recovery request at 600 ms
  could therefore be followed by an unnecessary heartbeat at 1,000 ms. Complete
  response writes now refresh that clock, moving this example's deadline to
  1,600 ms. Receive-only input and failed/partial writes retain their behavior.
* **Rust failed replay reset:** the analogous failed reset retained a listener
  backed by a faulted file journal or missing mapping. The runner now uses its
  existing retirement path, closing the child and listener before releasing
  replay ownership. The old session ID/frontier survive until a successful
  reopen and negotiation; standby and reopen errors cannot resume admission.

* **C++ and Rust exhausted multi-acceptor admission:** recovered outbound exhaustion
  marks a peer rejected and returns CONFIG_ERROR. Both owners retained the
  rejected peer, and later polls refused it forever. The owner cleanup condition
  now includes rejected status while preserving sibling peers, healthy preflight
  behavior and the direct peer pump's deferred cleanup contract. Both flavours reproduced the defect in all six triggering cases, and all
  twelve cases passed after each fix.

The heartbeat cases demonstrate removal of premature traffic, with byte-identical
recovery requests and preserved sequence state. No elapsed-time latency gain is
claimed, and the fixes introduce no application-send allocation or lock.

## Verification

Exact case identities, assertion failures, process results and skipped/ignored
inventories are checked independently. Each original/fixed pair uses the same
frozen regression fixture.

| Gate | Result |
|---|---|
| C++ reset original | 8 passed, 8 failed; 108 failed assertions |
| C++ restart original | 20 passed, 8 failed; 56 failed assertions |
| C++ focused normal / sanitizers / Clang / no exceptions or RTTI | 16 reset and 28 restart cases passed in each variant |
| Initial complete C++ open edition on BEELINK2 | 166 binaries passed; zero skips |
| Initial selected private C++ integrations on BEELINK2 | Exact unchanged 11 binaries passed; zero skips |
| Java original, native required | 53 passed, 14 failed; zero errors/skips |
| Java focused final | 67 passed in each of three configured executions; zero errors/skips |
| Java development | Exact 6,588 passed; JNI required; zero final failures/errors/skips |
| Java open | Exact 6,120 passed; JNI required; zero final failures/errors/skips |
| Rust restart original | 10 passed, 8 failed; zero ignored |
| Rust restart focused default / no-default / open with service packs | 18 passed in each |
| Rust reset original | 8 passed, 8 failed at retained listener; zero ignored |
| Rust combined reset/restart focused final | 16 reset and 18 restart cases passed in each of default, no-default and open/service-packs configurations |
| C++ multi-acceptor exhaustion original / focused final | Original: 6 passed, 6 failed with 18 failed assertions; final: 12 cases / 1,024 assertions passed in each of four variants |
| Rust multi-acceptor exhaustion original / focused final | Original: 6 passed, 6 failed at rejected-slot reclamation; final: 12 passed in each of three configurations |
| Rust full default / no-default at `9bdec5bab` | 1,923 passed in each; one existing ignored doctest; zero failures |
| Rust Clippy default / service packs at `9bdec5bab` | Both passed with warnings denied |
| Export scan | All 16 changed engine files, zero findings |
| C++ integration on hp at `cacc9bad2` | 168 open binary exits passed with two multicast round-trip skips; 11 selected private binaries passed with zero skips |
| Supplemental multicast on hp | Both real FIX and SBE round trips passed on `127.0.0.1`; original two automatic-interface skips retained |
| Java JNI integration on hp at `cacc9bad2` | 84 development / 81 open cases passed in each of three executions; native rebuilt and required; zero failures/errors/skips |
| Rust affected-path integration on hp at `cacc9bad2` | Exact 131 default, 131 no-default and 118 open/service-pack cases passed; zero failures/ignored |
| Rust publication integration on hp at `41363035a` | Exact 137 default, 137 no-default and 124 open/service-pack cases passed; zero failures/ignored |
| C++ transport publication supplement at `7678edc14` | Eight binaries, including all 56 local FIXP cases, and both real loopback multicast probes passed; zero skips |
| JNI publication supplement at `7678edc14` | 84 development / 81 open cases passed in each of three executions; both native libraries rebuilt and required; zero failures/errors/skips |

C++ fixtures use deterministic transport/store hooks and public runner lifecycle
and encoded protocol messages; they do not seed protocol/finalization state.
The reset fixture models both a faulted open file journal and an unmapped store.
The multi-acceptor exhaustion fixtures model the zero successor sentinel returned
by an exhausted mmap/custom durable provider; they do not reproduce file-journal
sequence exhaustion. C++ keeps its callback generation guard ahead of cleanup,
and both owners retain the direct peer pump's existing deferred cleanup contract.
JNI integration includes the existing native single/multi-acceptor cases, with
development-only mmap controls included in the development selection.
Rust restart and reset tests use real loopback TCP and tracking/model stores. Java
admission tests exercise direct multi, real TCP single and real TCP multi owners;
its clock cases use deterministic transport outcomes and injected timestamps.
These checks do not claim physical disk-failure or competing-process HA tests.

Final Java inventories include the native-loading guard and all previous test
identities. Three counter-disabled controls skip in the default execution and
pass in their explicit disabled execution. Rust retains the existing ignored
`builders.rs` doctest.

Two incomplete Rust compile attempts were stopped with SIGTERM. The first
stopped after other sessions started heavy builds concurrently. The second
stopped before source edits because the queued driver had loaded before a newly
added regression checkpoint; its retry starts with both new reproducers. No
tests ran in either interrupted attempt. Their logs and process evidence are
retained separately and excluded from pass totals. The full Rust suites and
Clippy subsequently completed through the process/memory guard at `9bdec5bab`.

The upstream C++ metrics change (`c8395eb93`) adds one open test binary.
Post-rebase checks rebuilt and ran 168 open and 11 selected private binaries,
then rebuilt JNI and checked 84 focused development and 81 open Java cases. The normal
upstream callback-stop cases are included; its optional worker-exit injection mode
is outside this rerun. The full Java inventories above precede the rebase.
Upstream changes leave the Java source/tests unchanged. The second rebase adds upstream Rust metrics callback
shutdown handling (`6243fdb30`) and sixteen regression cases, included in the final
full Cargo inventory. Additional Rust Makefile/tool changes concern optimized
PGO/BOLT builds, outside these Cargo commands. Independent checkpoints bind the
full Rust tree and seven relevant file hashes before and after integration;
per-command records separately bind the five locally changed Rust paths.

A third rebase incorporates upstream C++ transport portability and configuration
changes (`6d99e20bc`, `54e586b6d`), Rust configuration aliases and classic
initiator reconnect retirement (`f1955fda5`). The C++ binary inventory remains
168/11; its source guards now include the changed shared headers and fixtures,
and its reuse-port runtime case is checked explicitly. The private mmap handoff
fixture changed upstream but is outside the selected eleven private binaries.
The completed full Rust and Clippy evidence remains tied to `9bdec5bab`. Final
Rust integration on `hp` reran the 46 local FIXP regressions, 17 upstream
connection lifecycle cases and the affected configuration/initiator unit tests.
The open edition excludes six private-only initiator cases; exact case checking
uses their source attributes, including attributes separated from tests by comments.
A source-bound correction and the original expectation manifest are both retained.
The raw native transport diagnostic split one named result line; the checker
requires its explicit following status rather than inferring a pass.

Service-pack Clippy was suspended from 17:07:23 to 17:15:13 UTC while another
session built, then resumed and completed without source edits. A potential
integration checkout on `scan` was cancelled while queued behind a reserved
failover test; no heavy job ran there. Its checkout and scratch were removed
after retaining their records. The local final-integration run passed 131 default Rust cases, then its idle
coordinator was stopped while waiting for other sessions. Those completed results
are retained separately. Initial `hp` integration used four C++ jobs, one Cargo job, JDK 21 and GCC
toolset 14 at `cacc9bad2`. Publication integration uses the later `41363035a`
revision and a separately frozen Rust source inventory. Those six commands include
the upstream TCP hint/connect-timeout changes and descriptor-identity fixture
correction, with 63 integration cases and 74/74/61 affected unit cases.

The final source rebase incorporates C++ socket hints and connection deadlines
(`1f99e8887`, `5932c3743`). The bounded supplement rebuilds the three local FIXP
regressions and five changed upstream transport/configuration fixtures, plus the
two unchanged loopback multicast variants. JNI is rebuilt in both editions
because it instantiates the changed shared socket types. The 84/81 JNI selections
cover FIXP lifecycle and reaccept behavior; C++ checks the configurable classic/TLS
options. Java and Rust source trees remain unchanged by this last C++ rebase.

The final C++ run selected the first multicast-capable physical interface, which
joined successfully but did not loop traffic back on `hp`. Two scratch variants
change only interface selection to `127.0.0.1`; the original join/send/receive and
FIX/SBE payload assertions are unchanged and passed. This is supplementary
coverage, not a claim that the original two skips disappeared. Their forced
fresh build used two jobs. A missing `rg` diagnostic affected only generated
Makefile prerequisites; independent Python hashing bound every public header
before and after the forced rebuild. The scoped checker also handles Python
3.9 Counter zero-key equality without changing case expectations.

## Evidence and publication

Publication base: `292625a1658d89178ebcc5e544804a196eb1f236`.

Archive: `/home/yann/archive/libhft/reviews/2026-10-11-codex-review-acceptor-1011`,
mirrored at the same path on `hp`. It retains original/candidate/final source,
fixtures, exact case inventories, full logs/XML reports, process metadata,
independent checkers and a git bundle. After the first rebase, all eleven FIXP
source/fixture hashes remained identical.
The Makefile incorporates the upstream metrics test, with its merged inventory
verified separately. Two further source/test pairs cover exhausted multi-acceptor
admission, and C++ registers the added fixture.

Code commits: `9bb3b9b62` (C++), `57c0c2ba4` (Java), `027a220e6` (Rust),
`06861d7e8` (C++/Rust multi-acceptor admission). The publication rebase preserves
all four patches and all sixteen tested engine-file hashes byte for byte. The
entire Java tree remains identical to the initial completed `hp` integration tree;
the entire Rust tree is identical to the completed publication integration at
`41363035a`. The later C++ transport changes receive the bounded supplement
described above. Subsequent upstream HTTP scrape-renderer fixes
(`df732a17b`, `bfbc04c45`) affect neither the owned FIXP paths nor the
JNI/eight-fixture include closures. Their new renderer cases are not counted
in this review; their separate upstream record documents them. The retained
C++/JNI results remain pinned to `7678edc14`. A separate
`tested-revision-history.bundle` retains the earlier tested revisions as well.
`publication-tested-revisions.bundle` names the full-Rust, hp-integration and
publication-integration commits explicitly for restoration.
`cpp-jni-publication-revision.bundle` preserves the later `7678edc14` source revision.
