1#include <cx/net/socket.cxh>
2#include <cx/net/filter.cxh>
3#include <cx/net/pool.cxh>
4#include <cx/net/net_shared.h>
6/// @addtogroup net_flow
8/// A flow is the unit of event ordering.
10/// For a TCP socket, the obvious ordering domain is the socket itself, and that works fine. It
11/// falls apart for a single UDP socket serving many clients: if the socket were the ordering
12/// domain, every client's events would serialize behind one another and the thread pool would
13/// sit idle. So the ordering domain is the flow instead:
16/// TCP socket -> exactly one flow (ordering domain = the connection)
17/// UDP socket -> N flows (ordering domain = the peer address)
20/// The guarantee is: events belonging to one flow are strictly ordered and never run
21/// concurrently. Events on different flows may run at the same time on different workers, and
22/// nothing is promised about ordering *between* flows.
24/// Both socket types dispatch through the same mechanism, so there is one code path rather than
25/// two. For a single stream connection, the flow is created automatically during socket setup
26/// and shows up only as a field on the event; consumers that only care about one connection can
27/// ignore it entirely.
29/// @section net_flow_creation Creation
31/// A datagram flow is created automatically for the first packet from a new peer or the first
32/// packet sent to one, or explicitly with netqueuePromoteFlow(). Any of those fires NET_FlowOpen
33/// on the new flow, ordered ahead of its first NET_DataReceived -- the place to set up
34/// `flow->user` state and register per-flow handlers before any packet is seen. When the queue is
35/// at its flow cap and nothing is reclaimable, no flow is created: a received packet goes to the
36/// NET_FlowRefused handler instead, which can validate it and admit the peer with
37/// netqueuePromoteFlow(), and a send to an unknown peer is refused.
39/// Stream flows do not fire NET_FlowOpen; their session start is NET_Connection or NET_Accepted.
41/// However a flow comes into being, if its socket has filters attached the flow's own filter chain
42/// is built before the flow becomes visible, so no data can ever reach the application unfiltered.
43/// See @ref net_filter.
45/// @section net_flow_state Application state
47/// `flow->user` is the place for per-peer application state -- strings, buffers, objects,
48/// anything with a destructor. Because the flow is a real object, its generated destructor tears
49/// that state down automatically, so there's no need to hand-write cleanup for every way a flow
52/// Only flows carry a `user` array. Per-socket and per-queue state should go in the ctx pointer
53/// captured at handler registration instead, because sockets and queues don't have the same
54/// one-worker-at-a-time guarantee a flow does -- a single datagram socket can have thousands of
55/// flows running on every worker at once, so a shared array there would need its own locking.
57/// @section net_flow_timers Timers
59/// A flow is also the unit of timing. netflowAddTimer() arms a deadline and NET_Timer is delivered
60/// on the flow when it elapses -- on a worker, ordered behind everything already pending, exactly
63/// Timers belong to the flow's lifetime as well as its ordering: teardown cancels whatever is still
64/// armed, so no NET_Timer follows NET_FlowClosed.
66/// @section net_flow_teardown Teardown
68/// NET_FlowClosed is delivered as a terminal event on the flow's own queue, so it arrives after
69/// every packet the application has already been handed. It fires exactly once per flow, on
70/// every close cause, with the cause given in NetCloseReason.
72/// A flow reclaimed under cap pressure can resurrect: if a packet arrives before its terminal
73/// event has been delivered, the flow comes back to life and continues as if nothing happened.
74/// This avoids tearing down and immediately recreating a client that never actually left. Only
75/// reclaim works this way -- every other close reason is final, and a later packet cannot undo
78/// A single ordering domain: one connection, or one datagram peer
81 /// @brief Socket that owns this flow
83 /// Weak, to break the socket/flow ownership cycle. Resolve to a strong reference once per
84 /// dispatch batch, not once per packet.
85 weak[NetSocket] socket;
87 /// @brief Pool every message on this flow was drawn from, held strongly
89 /// Strong where `socket` is weak, and for the opposite reason: a flow can still be holding
90 /// messages -- in its inbox, its ready list, or the datagram staging queue -- at a moment when
91 /// neither its socket nor the queue is reachable any more, and a pooled buffer destroyed
92 /// instead of returned costs the whole queue that much of its ceiling for good. NULL only for a
93 /// stream flow whose socket has not been added to a queue yet; netqueueAddSocket() fills it in.
96 NetAddr peer; ///< Datagram: the peer address this flow is keyed on
98 /// @brief QUIC: the stream id this flow is keyed on. Zero on every other kind of flow.
100 /// A QUIC socket's flow table is keyed on this rather than on `peer`, because a QUIC
101 /// connection's identity is its Connection ID and its peer address can change underneath it.
104 atomic[uint32] claimed; ///< A worker is currently draining this flow
105 atomic[uint32] queued; ///< Present on the queue's runqueue
106 atomic[uint32] dying; ///< Marked for close; blocks further dispatch
108 /// @brief Pending messages for this flow, in an intrusive lock-free stack
110 /// Any ingest thread can push; only the worker holding the claim drains it.
113 NetMessage *ready; ///< Consumer-private FIFO head, refilled from inbox
114 NetMessage *readytail; ///< Consumer-private FIFO tail, for O(1) append
116 /// @brief Last time a packet was ingested for this flow, for approximate LRU
118 /// Reclaim scans for the oldest entries rather than keeping an ordered list, since updating a
119 /// list on every packet would put a lock on the hottest path in the system. Stored as a
120 /// coarse ~1 second tick rather than a precise timestamp, which keeps it small enough to
121 /// update as a single atomic write.
122 atomic[uint32] lastActive;
124 /// @brief Application state for this peer, allocated lazily
126 /// Often unused on stream sockets, where per-connection state usually lives in the ctx.
127 [noinit] sarray[stvar] user;
129 NetHandlers *handlers; ///< Per-flow handler overrides, optional
130 void *handlerCtx; ///< Context passed to per-flow handlers, set by setHandlers()
132 /// @brief Context passed to per-flow handlers, set by setHandlersObj() -- NULL when
133 /// handlerCtx is in use instead
134 weak[ObjInst] handlerWeak;
136 /// @brief Guards handlers/handlerCtx/handlerWeak against a concurrent setHandlers()/setHandlersObj()
139 /// @brief Ids of the timers currently armed on this flow, guarded by the queue's timerLock
140 [noinit] sarray[uint64] timers;
142 /// @brief This flow's filter chain, ordered application -> wire, or empty for none
144 /// Never installed by hand: the socket builds it when the flow is created (and when a filter is
145 /// attached to a socket that already has flows) by walking its own NetFilter list and calling
146 /// createFlow() on each, so the two run in lockstep. See @ref net_filter.
147 [noinit] sarray[object[NetFlowFilter]] filters;
149 /// @brief Stream: staging ring the encode chain consumes from, allocated with the chain
151 /// netsocketSend() writes the application payload here and the chain's first stage reads it;
152 /// whatever a stage declines to consume stays put and resumes on the next pass. NULL when the
153 /// flow has no filters, and always NULL on a datagram flow, which stages whole messages in
154 /// encInMsgs instead.
157 /// @brief Datagram: staging queue the encode chain consumes from, the encIn of the message side
159 /// A stage still negotiating declines to consume application messages, and they wait here until
160 /// it will take them, exactly as unconsumed bytes wait in a stream flow's encIn. Bounded by
161 /// NET_FLOW_ENCQ_MAX messages, past which a send is refused rather than buffered without limit.
162 NetMsgQueue encInMsgs;
164 /// @brief Serializes filter chain access for this flow
166 /// Decode always runs on the worker holding the flow's claim, but encode is driven by whatever
167 /// thread called netsocketSend(), and both touch the same stage objects and boundary buffers.
168 /// Held across an entire driver pass -- but never across an application callback, which could
169 /// re-enter through netsocketSend().
172 uint8 closeReason; ///< NetCloseReason once the flow is dying
174 factory create(NetSocket *socket, const NetAddr *peer);
175 init(); // needed to init lastActive
179 /// Marks the flow dying and queues a terminal NET_FlowClosed event behind everything already
180 /// pending for it. The flow is not freed here; it stays alive until its queued packets and
181 /// its terminal event have been delivered.
183 /// A flow closed this way cannot resurrect -- an arriving packet will not cancel the
184 /// teardown, and the peer is treated as a new, unknown source afterwards.
186 /// @return true if this call marked the flow dying, false if it was already closing
189 /// Register per-flow handler overrides
191 /// Fields left NULL fall through to the socket's set, and then to the queue-wide set. The
192 /// handler struct is not copied -- it must outlive the flow, which is why it is usually
195 /// @param handlers Handler set, or NULL to clear
196 /// @param ctx Context passed to these handlers on NetEvent.ctx
197 unbound void setHandlers([in] [opt] const NetHandlers *handlers, [in] [opt] void *ctx);
199 /// Register per-flow handler overrides, with an object as the context
201 /// Same as setHandlers(), except ctx is held weakly rather than borrowed.
203 /// @param handlers Handler set, or NULL to clear
204 /// @param ctx Object passed to these handlers on NetEvent.ctx, held weakly; NULL to clear
205 unbound void setHandlersObj([in] [opt] const NetHandlers *handlers, [in] [opt] ObjInst *ctx);
207 /// Send data on this flow
209 /// For a datagram flow this is a convenience wrapper that calls netsocketSend() on the owning
210 /// socket with this flow's peer as the destination -- filtering, backpressure, and queueing
211 /// are exactly netsocketSend()'s. A QUIC stream flow sends on its stream instead, since a
212 /// stream is named by its id and not by an address.
214 /// @param data Payload to send (copied; need not outlive the call)
215 /// @param len Length of the payload in bytes
216 /// @param flags Optional NetSocketOpFlags
217 /// @return true if the payload was sent or queued, false if it was refused
218 bool send([in] const uint8 *data, size_t len, flags_t flags);
220 /// Arm a timer on this flow
222 /// NetEvent.timer.id on the delivered NET_Timer carries the returned id, so one handler can
223 /// tell several timers apart.
225 /// @param delay Microseconds from now until the timer fires (see timeS() / timeMS())
226 /// @param flags Optional NetTimerFlags (NTF_Repeat to re-arm automatically)
227 /// @return The new timer's id, or 0 if the flow is closing or has no queue
231 /// // fail the request if the response has not completed within 30 seconds
232 /// NetTimerId deadline = netflowAddTimer(ev->flow, timeS(30), NTF_None);
234 unbound NetTimerId addTimer(int64 delay, flags_t flags);
236 /// Cancel a timer armed on this flow
238 /// @param id Timer id from netflowAddTimer()
239 /// @return true if this call stopped the timer before it fired. false if the timer had
240 /// already fired or was never armed; if two threads race to cancel the same timer, only
241 /// one of them gets true.
242 unbound bool cancelTimer(NetTimerId id);
244 /// Move an armed timer's deadline forward, keeping its id
246 /// @param id Timer id from netflowAddTimer()
247 /// @param delay Microseconds from now until the timer fires
248 /// @return false if the timer is no longer armed
249 unbound bool rearmTimer(NetTimerId id, int64 delay);
251 // -----------------------------------------------------------------------------------------
252 // PRIVATE IMPLEMENTATION DETAILS
254 // Everything below this point is internal plumbing between the net module's own translation
255 // units. It is not a stable API and carries no compatibility promise -- signatures and
256 // semantics change whenever the implementation needs them to. Nothing outside cx/net/ and the
257 // platform backends should call any of it.
258 // -----------------------------------------------------------------------------------------
262 // Push a message onto the flow's inbox. Returns true if the caller is responsible for putting
263 // the flow on the runqueue, which is the case only when it was not already there.
264 [sal _Check_return_] unbound bool _push([inout] NetMessage *msg);
266 // Pop the next message in FIFO order, refilling the private ready list from the inbox when it
267 // runs dry. May ONLY be called by the worker holding the flow's claim.
268 [sal _Ret_maybenull_] unbound NetMessage *_pop();
270 // Put a popped message back at the head of the ready list, so the next _pop() returns it
271 // again. For a worker that has to stop draining partway through -- the message has to keep its
272 // place, and everything behind it keeps its place too. Same rule as _pop(): the claim holder
274 unbound void _unpop([inout] NetMessage *msg);
276 // Mark the flow dying and queue its terminal NET_FlowClosed event. Returns false if the flow
277 // was already closing. The public close() is this with NCR_AppClosed.
278 unbound bool _close(NetCloseReason reason);
280 // Resolve the flow's queue through socket->queue. Returns a strong reference the caller must
281 // release, or NULL if either weak arm has already been broken.
282 [sal _Ret_maybenull_] unbound NetQueue *_queue();
284 // Flow table (flow.c)
286 // Snapshot a socket's live flows into `out`, each with a reference held, and hand the array to
287 // the caller to destroy. Initializes `out`; it must not already hold an array. Taken under the
288 // socket's flow lock and meant to be used outside it, because everything worth doing to a flow
289 // -- installing or dropping a filter chain, closing it -- allocates, primes, or reaches the
290 // wire, none of which may run with the flow table locked. Declared here rather than on
291 // NetSocket because sa_NetFlow is only visible from this side of the socket/flow include
292 // cycle, and because the rest of the flow table lives here too.
293 standalone void _snapshotFlows([in] NetSocket *sock, [out] sa_NetFlow *out);
295 // Filter chain construction (filter.c)
297 // The socket owns a list of NetFilter factories; every flow it owns carries the parallel list
298 // of NetFlowFilter stages they produce. The chain is always built before a flow is reachable
299 // by anything else, so there is no window in which data could move past a flow that should
300 // have been filtered. Everything here is serialized by the flow's filterLock.
302 // Create one socket filter's stage for this flow and append it to the chain, allocating the
303 // staging buffer if this is the flow's first stage. A factory that returns NULL contributes
305 [extern] unbound void _addFilter([inout] NetSocket *sock, [in] NetFilter *filter);
307 // Build the whole chain by walking the socket's filter list in order. A no-op for a socket
308 // with no filters, which is what keeps an unfiltered flow free of any filter cost at all.
309 [extern] unbound void _buildFilters([inout] NetSocket *sock);
311 // Drop the flow's chain, its staging storage, and anything the stages had buffered.
312 [extern] unbound void _clearFilters();
314 // Begin an orderly close on every stage (app -> wire). The caller runs one more encode pass
315 // afterwards to put whatever they produced (a TLS close_notify) on the wire.
316 [extern] unbound void _filterShutdown();
318 // Deliver every notification the chain has raised, clearing each as it goes. `onWorker`
319 // delivers inline -- correct only from inside the flow's dispatch batch, where it also orders
320 // the notification ahead of data produced by the same pass; otherwise each is queued on the
321 // flow so it still lands on a worker in order. Must NOT be called with the flow's filterLock
322 // held: a handler can call straight back into the send path.
323 [extern] unbound void _filterNotify([in] [opt] NetQueue *q, [in] [opt] NetSocket *sock,
326 // Filter data plane (dataplane.c)
328 // The four functions where a filtered flow's chain actually runs, all of them on the shared
329 // send/recv path rather than in any backend. A stage never touches the wire itself: these
330 // drivers own the socket's buffers and syscalls on its behalf, which is what stops a stage
331 // from bypassing one above it in the chain. Each takes the flow's filterLock for the duration
332 // of the chain walk and drops it before anything can call back into application code.
334 // Stream send: stage `data` (NULL/0 for a priming pass) into the flow's encIn, run the encode
335 // chain repeatedly until no stage has more to produce -- each stage reading the previous
336 // stage's encOut -- and flush what the wire-end stage produced onto the socket's send chain.
337 // Returns false on a fatal transform error, having closed the flow.
338 [extern] unbound bool _filterStreamSend([in] [opt] NetQueue *q, [inout] NetSocket *sock,
339 [sal _In_reads_bytes_opt_(len)] const uint8 *data, size_t len);
341 // Stream recv: run the decode chain repeatedly, until no stage has more to produce, over the
342 // raw bytes already in the socket's receive ring -- each stage reading the decOut of the
343 // stage one hop toward the wire -- push out any wire-bound output the pass produced, deliver
344 // the stages' notifications, and deliver NET_DataReceived if the head stage produced
345 // application bytes. Runs on a worker (drainFlow).
346 [extern] unbound void _filterStreamRecv([in] [opt] NetQueue *q, [inout] NetSocket *sock);
348 // Encode half of the datagram send: queue `data` (NULL/0 for a priming pass) on the flow's
349 // staging queue, run the encode chain repeatedly until no stage has more to produce, and
350 // collect what the wire-end stage produced into `out` rather than sending it. Split from the
351 // send below so the chain can be driven without a socket. `fatalp` distinguishes a stage
352 // failing (the flow is finished) from a payload merely being refused (the caller backs off);
354 [extern] unbound bool _filterDatagramEncode([in] [opt] NetQueue *q,
355 [sal _In_reads_bytes_opt_(len)] const uint8 *data, size_t len,
356 [inout] NetMsgQueue *out, [out] [opt] bool *fatalp);
358 // Datagram send: queue `data` (NULL/0 for a priming pass) on the flow's staging queue, run
359 // the encode chain repeatedly until no stage has more to produce, and send every message the
360 // wire-end stage produced to the flow's peer. Returns false if the payload could not be
361 // accepted (staging queue full, pool exhausted) or a stage failed fatally.
362 [extern] unbound bool _filterDatagramSend([in] [opt] NetQueue *q, [inout] NetSocket *sock,
363 [sal _In_reads_bytes_opt_(len)] const uint8 *data, size_t len);
365 // Datagram recv: run one received message through the decode chain (consuming it), send any
366 // wire-bound output the pass produced, and append whatever came out the application end to
367 // `out` for the caller to deliver. Runs on a worker (drainFlow). Returns false on a fatal
369 [extern] unbound bool _filterDatagramRecv([in] [opt] NetQueue *q, [inout] NetSocket *sock,
370 [inout] NetMessage *msg, [inout] NetMsgQueue *out);
372 // Run the encode side once with nothing staged, so a filter that initiates a negotiation gets
373 // to emit its first flight. Called when a chain is built (datagram), when the transport comes
374 // up (stream), and after shutdown() to flush a close record. A no-op for an unfiltered flow,
375 // or a stream flow whose socket is not connected yet.
376 [extern] unbound void _primeFilters([in] [opt] NetQueue *q, [inout] NetSocket *sock);