feat(network): support a caller-owned poll - #153
Merged
Conversation
Separate the TCP network state from Poll by storing a Registry in NetworkState. TcpNetwork continues to own its Poll and Events, while shared network operations move to TcpNetworkRaw. Add TcpNetworkWithExternalPoll so a caller can register TCP sockets with its own poll. The caller drives reconnect processing, passes relevant events to the network, and drains pending disconnect notifications through pre_poll, epoll_event, and post_poll. The external-poll constructor accepts a half-open token range. Listener and connection tokens are assigned from that range in ascending order, and an operation requiring another token panics when the range is exhausted. The caller remains responsible for avoiding collisions with other tokens registered with the same poll. TcpNetwork and TcpNetworkWithExternalPoll expose the shared operations through Deref. Ordinary method-call syntax remains available, but fully qualified calls to methods previously defined directly on TcpNetwork will need updating. Co-authored-by: vladimir-ea <vladimir@gattaca.com> Assisted-by: Claude:claude-fable-5 Assisted-by: Codex:gpt-5.6-sol
Export TcpNetworkCore and TcpNetworkWithExternalPoll. Rename the shared type from TcpNetworkRaw to TcpNetworkCore and rename epoll_event to the platform-neutral handle_event. Document the internally polled and caller-polled variants, including the pre_poll, poll, handle_event, and post_poll sequence. Add a debug assertion to detect events routed outside the network's configured token range. Add integration coverage for two TCP networks driven by one Poll with disjoint token ranges. The test exercises bidirectional messages, reconnection with a stable outbound token, and disconnect notification delivery. Additional tests cover token-range exhaustion and the owned network's shared listener/connection token counter. Assisted-by: Claude:claude-fable-5 Assisted-by: Codex:gpt-5.6-sol
Add a compiling example for TcpNetworkWithExternalPoll showing the pre_poll, poll, token dispatch, handle_event, and post_poll sequence. Add coverage for dispatching an event outside the network's configured token range. Debug builds reject it through the containment assertion; release builds emit no TcpEvent for the unknown token. Assisted-by: Claude:claude-fable-5 Assisted-by: Codex:gpt-5.6-sol
vladimir-ea
reviewed
Sep 4, 2026
| /// [`Self::handle_event`] for each event in this network's token range, and | ||
| /// [`Self::post_poll`]. The caller owns the poll, event buffer, and timeout. | ||
| /// Dropping this value drops its sockets but does not explicitly deregister | ||
| /// them. |
Contributor
There was a problem hiding this comment.
does it not deregister them? the TcpNetworkCore#state has the Registry - we could add a Drop impl to NetworkState that deregisters on drop? this would be harmless for the TcpNetwork that owns its own Poll
Contributor
Author
There was a problem hiding this comment.
Thanks. Will also close_connection_socket - which is a minor behaviour change for TcpNetwork but I think a useful one.
ltitanb
approved these changes
Sep 4, 2026
| repository = "https://github.com/gattaca-com/flux" | ||
| rust-version = "1.91.0" | ||
| version = "0.2.0" | ||
| version = "0.2.1" |
Contributor
There was a problem hiding this comment.
think we merged already 0.3.0
Attempt to deregister registered connection sockets and listeners when NetworkState is dropped. This matters for a caller-owned poll because, on Linux, a duplicated descriptor can keep an epoll registration alive after the original descriptor closes. Order TcpNetwork's core before its poll so network cleanup runs while the owned poll is still alive. Add a Linux-only regression test that keeps a duplicated listener descriptor open and verifies that dropping the state removes its listener and endpoint registrations. Assisted-by: Claude:claude-fable-5 Assisted-by: Codex:gpt-5.6-sol
vladimir-ea
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TcpNetworkCorenow holds state and operations shared by two wrappers.TcpNetworkretains its internalPoll,Events, andpoll_with; the newTcpNetworkWithExternalPollregisters sockets through a caller-providedRegistry. Both wrappers expose core operations throughDeref.This allows a caller to drive multiple TCP networks from one
Polland route readiness events by token.External polling contract
Each polling cycle has four steps:
pre_pollto deliver pending disconnect notifications and attempt due reconnects.PollandEventsbuffer.handle_event.post_pollto deliver pending disconnect notifications.If
post_pollis skipped, pending notifications remain queued for a laterpost_pollorpre_poll. The caller chooses the poll timeout. Dropping the network drops its sockets but does not explicitly deregister them.TcpNetworkWithExternalPollincludes a compiling rustdoc example of the loop.Token ranges
TcpNetworkWithExternalPoll::newtakes a half-openRange<usize>. Listener and connection tokens are assigned from that range in ascending order and are not reused; persistent outbound endpoints retain their token across reconnects. An operation that requires another token panics when the range is exhausted. This replaces the previous check, which guarded only againstusizeoverflow.Example in rustdoc is suggesting 2^48 tokens in a token range: this number might seem too large for most uses, but there is literally no cost to using such a large range. The example aims to establish a good practice (range exhaustion causes panic).
handle_eventuses adebug_assertto reject tokens outside the configured range in debug builds. The caller is responsible for assigning non-overlapping ranges and routing events to the appropriate network; overlapping ranges are not detected. The ownedTcpNetworkuses an internal range starting at zero, so its constructor is unchanged.Compatibility and versioning
Method-call syntax continues to work through
Deref, but moved methods are no longer available through fully qualified paths such asTcpNetwork::send_with. Version bumped to 0.2.1 (new feature:TcpNetworkWithExternalPoll)Tests
TcpEventin release builds.Assisted-by: Claude:claude-fable-5
Assisted-by: Codex:gpt-5.6-sol