nquic_protocol_streams_send (nquic v1.0.0)
View SourceSend-side stream drain engine and blocked/pending stream tracking.
Pure functions over #conn_state{} that turn buffered outbound stream
bytes into STREAM frames and maintain the indices that keep the flush
path O(1) in the number of idle streams.
The drain engine (drain_pending_sends/1, drain_round_robin/2,
drain_streams/3, drain_streams_capped/4, drain_one_stream/4,
drain_chunks/5, drain_chunks_loop/11, emit_lone_fin/5,
peel_bytes/2,3) walks the pending_send_streams index, sizing each
STREAM frame to fit one MTU-bounded short-header packet and capping the
total bytes per call by the connection cwnd headroom (cwnd_budget/1).
Frame sizing helpers are stream_frame_overhead/2 and
stream_frame_size/3.
Readiness/blocked tracking (check_writable_1byte/2, is_writable/2,
mark_blocked/2, clear_blocked/2, scan_blocked_streams/1,
scan_blocked_stream/2) detects flow/CC writable-edge transitions, and
the pending-send index (mark_pending_send/2, clear_pending_send/2,
has_pending_send/1, sync_pending_send/2, put_stream/3) plus peer
stream-id tracking (track_peer_stream_id/2) round out the send-side
bookkeeping. Terminal reclamation and MAX_STREAMS auto-extension are
delegated to nquic_protocol_streams_lifecycle.
Summary
Functions
Check that at least one byte can be sent on the given stream right now.
Remove StreamID from the pending-send index. Idempotent.
Call after a drain empties the buffer (and any FIN has been emitted) or
after cleanup_stream/reset_stream purge the buffer.
Walk all streams with buffered outbound data and produce STREAM frames.
Each frame is sized to fit in one MTU-bounded short-header packet and
is queued onto pending_app_frames. The per-connection cwnd headroom
caps the total bytes drained per call: anything that doesn't fit stays
in the stream's pending_send_data and is drained on the next flush
(typically triggered when an ACK frees bytes_in_flight).
Iterates the pending_send_streams index rather than the full streams
map, so connections with many idle streams pay only for the ones with
buffered bytes or a latched FIN.
Quick check: does any stream still have buffered outbound data or a latched FIN?
Public predicate: is this stream currently writable?
Unknown stream IDs return false. Applies the lazy stream-limit
initialisation used by send_stream/4 so freshly-opened streams report
accurately before the first send. Used by nquic_lib:is_writable/2.
Add StreamID to the pending-send index. Idempotent.
Call after any operation that grows pending_send_data or latches
pending_send_fin on a live stream. Maintaining the index allows
flush/1 and drain_pending_sends/1 to short-circuit in O(1).
Reconcile the pending-send index for StreamID against the stream's
current pending_send_size / pending_send_fin. Use after operations
that may have flipped membership in either direction.
Functions
-spec check_writable_1byte(#stream_state{stream_id :: nquic:stream_id(), type :: bidi | uni, send_state :: ready | send | data_sent | data_recvd | reset_sent | reset_recvd, send_offset :: non_neg_integer(), send_max_data :: non_neg_integer(), last_stream_data_blocked :: non_neg_integer(), pending_send_data :: [binary()], pending_send_size :: non_neg_integer(), pending_send_fin :: boolean(), recv_state :: recv | size_known | data_recvd | reset_recvd | data_read | reset_read, recv_offset :: non_neg_integer(), recv_max_offset :: non_neg_integer(), recv_window :: non_neg_integer(), recv_buffer :: gb_trees:tree(non_neg_integer(), {binary(), boolean()}), app_buffer :: iodata(), app_buffer_size :: non_neg_integer()}, nquic_protocol:state()) -> boolean().
Check that at least one byte can be sent on the given stream right now.
Runs the three send-side checks (connection flow, stream flow, congestion control) against a length of 1, gated on the stream's send state being still writable. Returns true iff all checks pass. Used to detect writable-edge transitions for previously flow/CC-blocked streams.
-spec clear_blocked(nquic:stream_id(), nquic_protocol:state()) -> nquic_protocol:state().
-spec clear_pending_send(nquic:stream_id(), nquic_protocol:state()) -> nquic_protocol:state().
Remove StreamID from the pending-send index. Idempotent.
Call after a drain empties the buffer (and any FIN has been emitted) or
after cleanup_stream/reset_stream purge the buffer.
-spec drain_pending_sends(nquic_protocol:state()) -> nquic_protocol:state().
Walk all streams with buffered outbound data and produce STREAM frames.
Each frame is sized to fit in one MTU-bounded short-header packet and
is queued onto pending_app_frames. The per-connection cwnd headroom
caps the total bytes drained per call: anything that doesn't fit stays
in the stream's pending_send_data and is drained on the next flush
(typically triggered when an ACK frees bytes_in_flight).
Iterates the pending_send_streams index rather than the full streams
map, so connections with many idle streams pay only for the ones with
buffered bytes or a latched FIN.
-spec has_pending_send(nquic_protocol:state()) -> boolean().
Quick check: does any stream still have buffered outbound data or a latched FIN?
-spec is_writable(nquic:stream_id(), nquic_protocol:state()) -> boolean().
Public predicate: is this stream currently writable?
Unknown stream IDs return false. Applies the lazy stream-limit
initialisation used by send_stream/4 so freshly-opened streams report
accurately before the first send. Used by nquic_lib:is_writable/2.
-spec mark_blocked(nquic:stream_id(), nquic_protocol:state()) -> nquic_protocol:state().
-spec mark_pending_send(nquic:stream_id(), nquic_protocol:state()) -> nquic_protocol:state().
Add StreamID to the pending-send index. Idempotent.
Call after any operation that grows pending_send_data or latches
pending_send_fin on a live stream. Maintaining the index allows
flush/1 and drain_pending_sends/1 to short-circuit in O(1).
-spec scan_blocked_stream(nquic:stream_id(), nquic_protocol:state()) -> {[nquic_protocol:event()], nquic_protocol:state()}.
-spec scan_blocked_streams(nquic_protocol:state()) -> {[nquic_protocol:event()], nquic_protocol:state()}.
-spec stream_frame_overhead(nquic:stream_id(), non_neg_integer()) -> pos_integer().
-spec stream_frame_size(nquic:stream_id(), non_neg_integer(), non_neg_integer()) -> pos_integer().
-spec sync_pending_send(nquic:stream_id(), nquic_protocol:state()) -> nquic_protocol:state().
Reconcile the pending-send index for StreamID against the stream's
current pending_send_size / pending_send_fin. Use after operations
that may have flipped membership in either direction.
-spec track_peer_stream_id(nquic:stream_id(), nquic_protocol:state()) -> nquic_protocol:state().