The per-stream credit and terminal-state machine, owned functionally
by Portals.Connection (like the pending-request table) rather than
by a process of its own.
Two completely independent windows:
- send window —
send_creditbytes the peer has granted us andsend_framesframes we have sent that the peer has not yet acknowledged, capped bymax_frames; - receive window —
recv_creditbytes we have granted the peer andrecv_framesframes it has sent that our owner has not yet consumed, capped bymax_frames.
Every byte figure is the complete encoded frame size, including the 4-byte length prefix.
Summary
Functions
Apply a CREDIT frame received from the peer.
Charge one outbound frame of bytes encoded bytes against the send
window. Never sends: the caller must only put the frame on the wire
when this returns {:ok, state}.
The owner consumed frames inbound frames totalling bytes encoded
bytes. Returns the replenished state and the number of bytes to grant
back to the peer in a CREDIT frame.
True when both directions are half-closed; the terminal frame may still be outstanding.
The initial (or replenishing) grant this side owes the peer: the
unused portion of our receive window. Returns {state, bytes};
bytes is 0 when there is nothing new to grant.
Record the peer's HALF_CLOSE of its sending direction. Idempotent.
Close only this side's sending direction. Idempotent.
A fresh stream with no credit in either direction.
Park a blocked sender until credit arrives.
Pop the next parked sender, if any.
Record one inbound STREAM_DATA frame of bytes encoded bytes.
Rejects anything the peer was not entitled to send.
Every parked sender, in order, leaving the queue empty.
Types
@type direction_state() :: :open | :closed
@type t() :: %Portals.Stream.State{ id: non_neg_integer(), inbound: direction_state(), max_credit: pos_integer(), max_frames: pos_integer(), outbound: direction_state(), owner: pid(), owner_ref: reference() | nil, recv_bytes: non_neg_integer(), recv_credit: non_neg_integer(), recv_frames: non_neg_integer(), send_credit: non_neg_integer(), send_frames: non_neg_integer(), send_waiters: :queue.queue() }
Functions
@spec add_send_credit(t(), non_neg_integer(), non_neg_integer()) :: {:ok, t()} | {:error, :credit_limit_exceeded}
Apply a CREDIT frame received from the peer.
@spec charge_send(t(), pos_integer()) :: {:ok, t()} | {:error, :outbound_closed | :insufficient_credit | :frame_limit}
Charge one outbound frame of bytes encoded bytes against the send
window. Never sends: the caller must only put the frame on the wire
when this returns {:ok, state}.
@spec consume_inbound(t(), non_neg_integer(), non_neg_integer()) :: {t(), non_neg_integer()}
The owner consumed frames inbound frames totalling bytes encoded
bytes. Returns the replenished state and the number of bytes to grant
back to the peer in a CREDIT frame.
True when both directions are half-closed; the terminal frame may still be outstanding.
@spec grant(t()) :: {t(), non_neg_integer()}
The initial (or replenishing) grant this side owes the peer: the
unused portion of our receive window. Returns {state, bytes};
bytes is 0 when there is nothing new to grant.
Record the peer's HALF_CLOSE of its sending direction. Idempotent.
Close only this side's sending direction. Idempotent.
@spec new(non_neg_integer(), pid(), keyword()) :: t()
A fresh stream with no credit in either direction.
Park a blocked sender until credit arrives.
Pop the next parked sender, if any.
@spec record_inbound(t(), pos_integer()) :: {:ok, t()} | {:error, :inbound_closed | :credit_exceeded | :frame_limit}
Record one inbound STREAM_DATA frame of bytes encoded bytes.
Rejects anything the peer was not entitled to send.
Every parked sender, in order, leaving the queue empty.