CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
flow.h
Go to the documentation of this file.
1
74
75#pragma once
76// This header file is auto-generated!
77// Do not make changes to this file or they will be overwritten.
78// clang-format off
79#include <cx/obj.h>
80#include <cx/struct.h>
81#include <cx/net/socket.h>
82#include <cx/net/filter.h>
83#include <cx/net/pool.h>
84#include <cx/net/net_shared.h>
85
86CX_C_BEGIN
87
88typedef struct NetFlow NetFlow;
89typedef struct NetFlow_WeakRef NetFlow_WeakRef;
90typedef struct NetFlow NetFlow;
91typedef struct NetFlow_WeakRef NetFlow_WeakRef;
93saDeclarePtr(NetFlow_WeakRef);
94#define _sti_NetFlow _sti_object
95#define SType_NetFlow NetFlow*
96#define STStorageType_NetFlow NetFlow*
97#define STypeArg_NetFlow(type, val) stgeneric(object, (ObjInst*)objInstCheckClass(NetFlow, val))
98#define STypeArgPtr_NetFlow(type, val) (stgeneric*)objInstCheckClassPtr(NetFlow, val)
99#define STypeCheckedArg_NetFlow(type, val) stType(type), stArg(type, val)
100#define STypeCheckedPtrArg_NetFlow(type, val) stType(type), stArgPtr(type, val)
101
102
103
104typedef struct NetFlow_ClassIf {
105 ObjIface* _implements;
106 ObjIface* _parent;
107 size_t _size;
108
109 // needed to init lastActive
110 bool (*close)(_In_ void* self);
111 bool (*send)(_In_ void* self, _In_ const uint8* data, size_t len, flags_t flags);
112} NetFlow_ClassIf;
113extern NetFlow_ClassIf NetFlow_ClassIf_tmpl;
114
116typedef struct NetFlow {
117 union {
118 NetFlow_ClassIf* _;
119 void* _is_NetFlow;
120 void* _is_ObjInst;
121 };
122 ObjClassInfo* _clsinfo;
123 atomic(uintptr) _ref;
124 atomic(ptr) _weakref;
125
130 Weak(NetSocket)* socket;
144 uint64 key;
145 atomic(uint32) claimed;
146 atomic(uint32) queued;
147 atomic(uint32) dying;
151 atomic(ptr) inbox;
160 atomic(uint32) lastActive;
164 sa_stvar user;
169 Weak(ObjInst)* handlerWeak;
171 sa_uint64 timers;
177 sa_NetFlowFilter filters;
184 BufRing* encIn;
199} NetFlow;
200extern ObjClassInfo NetFlow_clsinfo;
201#define NetFlow(inst) objInstCheckClass(NetFlow, inst)
202#define NetFlowNone ((NetFlow*)NULL)
203
204typedef struct NetFlow_WeakRef {
205 union {
206 ObjInst* _inst;
207 void* _is_NetFlow_WeakRef;
208 void* _is_ObjInst_WeakRef;
209 };
210 atomic(uintptr) _ref;
211 RWLock _lock;
212} NetFlow_WeakRef;
213#define NetFlow_WeakRef(inst) objWeakRefCheckClass(NetFlow, inst)
214
215_objfactory_guaranteed NetFlow* NetFlow_create(NetSocket* socket, const NetAddr* peer);
216// NetFlow* netflowCreate(NetSocket* socket, const NetAddr* peer);
217#define netflowCreate(socket, peer) NetFlow_create(NetSocket(socket), peer)
218
219void NetFlow_setHandlers(_In_ NetFlow* self, _In_opt_ const NetHandlers* handlers, _In_opt_ void* ctx);
230#define netflowSetHandlers(self, handlers, ctx) NetFlow_setHandlers(NetFlow(self), handlers, ctx)
231
232void NetFlow_setHandlersObj(_In_ NetFlow* self, _In_opt_ const NetHandlers* handlers, _In_opt_ ObjInst* ctx);
241#define netflowSetHandlersObj(self, handlers, ctx) NetFlow_setHandlersObj(NetFlow(self), handlers, ObjInst(ctx))
242
243NetTimerId NetFlow_addTimer(_In_ NetFlow* self, int64 delay, flags_t flags);
260#define netflowAddTimer(self, delay, flags) NetFlow_addTimer(NetFlow(self), delay, flags)
261
262bool NetFlow_cancelTimer(_In_ NetFlow* self, NetTimerId id);
271#define netflowCancelTimer(self, id) NetFlow_cancelTimer(NetFlow(self), id)
272
273bool NetFlow_rearmTimer(_In_ NetFlow* self, NetTimerId id, int64 delay);
281#define netflowRearmTimer(self, id, delay) NetFlow_rearmTimer(NetFlow(self), id, delay)
282
283_Check_return_ bool NetFlow__push(_In_ NetFlow* self, _Inout_ NetMessage* msg);
284// bool netflow_push(NetFlow* self, NetMessage* msg);
285//
286// -----------------------------------------------------------------------------------------
287// PRIVATE IMPLEMENTATION DETAILS
288// // Everything below this point is internal plumbing between the net module's own translation
289// units. It is not a stable API and carries no compatibility promise -- signatures and
290// semantics change whenever the implementation needs them to. Nothing outside cx/net/ and the
291// platform backends should call any of it.
292// -----------------------------------------------------------------------------------------
293// Inbox (flow.c)
294// Push a message onto the flow's inbox. Returns true if the caller is responsible for putting
295// the flow on the runqueue, which is the case only when it was not already there.
296#define netflow_push(self, msg) NetFlow__push(NetFlow(self), msg)
297
298_Ret_maybenull_ NetMessage* NetFlow__pop(_In_ NetFlow* self);
299// NetMessage* netflow_pop(NetFlow* self);
300//
301// Pop the next message in FIFO order, refilling the private ready list from the inbox when it
302// runs dry. May ONLY be called by the worker holding the flow's claim.
303#define netflow_pop(self) NetFlow__pop(NetFlow(self))
304
305void NetFlow__unpop(_In_ NetFlow* self, _Inout_ NetMessage* msg);
306// void netflow_unpop(NetFlow* self, NetMessage* msg);
307//
308// Put a popped message back at the head of the ready list, so the next _pop() returns it
309// again. For a worker that has to stop draining partway through -- the message has to keep its
310// place, and everything behind it keeps its place too. Same rule as _pop(): the claim holder
311// only.
312#define netflow_unpop(self, msg) NetFlow__unpop(NetFlow(self), msg)
313
314bool NetFlow__close(_In_ NetFlow* self, NetCloseReason reason);
315// bool netflow_close(NetFlow* self, NetCloseReason reason);
316//
317// Mark the flow dying and queue its terminal NET_FlowClosed event. Returns false if the flow
318// was already closing. The public close() is this with NCR_AppClosed.
319#define netflow_close(self, reason) NetFlow__close(NetFlow(self), reason)
320
321_Ret_maybenull_ NetQueue* NetFlow__queue(_In_ NetFlow* self);
322// NetQueue* netflow_queue(NetFlow* self);
323//
324// Resolve the flow's queue through socket->queue. Returns a strong reference the caller must
325// release, or NULL if either weak arm has already been broken.
326#define netflow_queue(self) NetFlow__queue(NetFlow(self))
327
328void NetFlow__snapshotFlows(_In_ NetSocket* sock, _Out_ sa_NetFlow* out);
329// void netflow_snapshotFlows(NetSocket* sock, sa_NetFlow* out);
330//
331// Flow table (flow.c)
332// Snapshot a socket's live flows into `out`, each with a reference held, and hand the array to
333// the caller to destroy. Initializes `out`; it must not already hold an array. Taken under the
334// socket's flow lock and meant to be used outside it, because everything worth doing to a flow
335// -- installing or dropping a filter chain, closing it -- allocates, primes, or reaches the
336// wire, none of which may run with the flow table locked. Declared here rather than on
337// NetSocket because sa_NetFlow is only visible from this side of the socket/flow include
338// cycle, and because the rest of the flow table lives here too.
339#define netflow_snapshotFlows(sock, out) NetFlow__snapshotFlows(NetSocket(sock), out)
340
341void NetFlow__addFilter(_In_ NetFlow* self, _Inout_ NetSocket* sock, _In_ NetFilter* filter);
342// void netflow_addFilter(NetFlow* self, NetSocket* sock, NetFilter* filter);
343//
344// Filter chain construction (filter.c)
345// // The socket owns a list of NetFilter factories; every flow it owns carries the parallel list
346// of NetFlowFilter stages they produce. The chain is always built before a flow is reachable
347// by anything else, so there is no window in which data could move past a flow that should
348// have been filtered. Everything here is serialized by the flow's filterLock.
349// Create one socket filter's stage for this flow and append it to the chain, allocating the
350// staging buffer if this is the flow's first stage. A factory that returns NULL contributes
351// nothing.
352#define netflow_addFilter(self, sock, filter) NetFlow__addFilter(NetFlow(self), NetSocket(sock), NetFilter(filter))
353
354void NetFlow__buildFilters(_In_ NetFlow* self, _Inout_ NetSocket* sock);
355// void netflow_buildFilters(NetFlow* self, NetSocket* sock);
356//
357// Build the whole chain by walking the socket's filter list in order. A no-op for a socket
358// with no filters, which is what keeps an unfiltered flow free of any filter cost at all.
359#define netflow_buildFilters(self, sock) NetFlow__buildFilters(NetFlow(self), NetSocket(sock))
360
361void NetFlow__clearFilters(_In_ NetFlow* self);
362// void netflow_clearFilters(NetFlow* self);
363//
364// Drop the flow's chain, its staging storage, and anything the stages had buffered.
365#define netflow_clearFilters(self) NetFlow__clearFilters(NetFlow(self))
366
367void NetFlow__filterShutdown(_In_ NetFlow* self);
368// void netflow_filterShutdown(NetFlow* self);
369//
370// Begin an orderly close on every stage (app -> wire). The caller runs one more encode pass
371// afterwards to put whatever they produced (a TLS close_notify) on the wire.
372#define netflow_filterShutdown(self) NetFlow__filterShutdown(NetFlow(self))
373
374void NetFlow__filterNotify(_In_ NetFlow* self, _In_opt_ NetQueue* q, _In_opt_ NetSocket* sock, bool onWorker);
375// void netflow_filterNotify(NetFlow* self, NetQueue* q, NetSocket* sock, bool onWorker);
376//
377// Deliver every notification the chain has raised, clearing each as it goes. `onWorker`
378// delivers inline -- correct only from inside the flow's dispatch batch, where it also orders
379// the notification ahead of data produced by the same pass; otherwise each is queued on the
380// flow so it still lands on a worker in order. Must NOT be called with the flow's filterLock
381// held: a handler can call straight back into the send path.
382#define netflow_filterNotify(self, q, sock, onWorker) NetFlow__filterNotify(NetFlow(self), NetQueue(q), NetSocket(sock), onWorker)
383
384bool NetFlow__filterStreamSend(_In_ NetFlow* self, _In_opt_ NetQueue* q, _Inout_ NetSocket* sock, _In_reads_bytes_opt_(len) const uint8* data, size_t len);
385// bool netflow_filterStreamSend(NetFlow* self, NetQueue* q, NetSocket* sock, const uint8* data, size_t len);
386//
387// Filter data plane (dataplane.c)
388// // The four functions where a filtered flow's chain actually runs, all of them on the shared
389// send/recv path rather than in any backend. A stage never touches the wire itself: these
390// drivers own the socket's buffers and syscalls on its behalf, which is what stops a stage
391// from bypassing one above it in the chain. Each takes the flow's filterLock for the duration
392// of the chain walk and drops it before anything can call back into application code.
393// Stream send: stage `data` (NULL/0 for a priming pass) into the flow's encIn, run the encode
394// chain repeatedly until no stage has more to produce -- each stage reading the previous
395// stage's encOut -- and flush what the wire-end stage produced onto the socket's send chain.
396// Returns false on a fatal transform error, having closed the flow.
397#define netflow_filterStreamSend(self, q, sock, data, len) NetFlow__filterStreamSend(NetFlow(self), NetQueue(q), NetSocket(sock), data, len)
398
399void NetFlow__filterStreamRecv(_In_ NetFlow* self, _In_opt_ NetQueue* q, _Inout_ NetSocket* sock);
400// void netflow_filterStreamRecv(NetFlow* self, NetQueue* q, NetSocket* sock);
401//
402// Stream recv: run the decode chain repeatedly, until no stage has more to produce, over the
403// raw bytes already in the socket's receive ring -- each stage reading the decOut of the
404// stage one hop toward the wire -- push out any wire-bound output the pass produced, deliver
405// the stages' notifications, and deliver NET_DataReceived if the head stage produced
406// application bytes. Runs on a worker (drainFlow).
407#define netflow_filterStreamRecv(self, q, sock) NetFlow__filterStreamRecv(NetFlow(self), NetQueue(q), NetSocket(sock))
408
409bool NetFlow__filterDatagramEncode(_In_ NetFlow* self, _In_opt_ NetQueue* q, _In_reads_bytes_opt_(len) const uint8* data, size_t len, _Inout_ NetMsgQueue* out, _Out_opt_ bool* fatalp);
410// bool netflow_filterDatagramEncode(NetFlow* self, NetQueue* q, const uint8* data, size_t len, NetMsgQueue* out, bool* fatalp);
411//
412// Encode half of the datagram send: queue `data` (NULL/0 for a priming pass) on the flow's
413// staging queue, run the encode chain repeatedly until no stage has more to produce, and
414// collect what the wire-end stage produced into `out` rather than sending it. Split from the
415// send below so the chain can be driven without a socket. `fatalp` distinguishes a stage
416// failing (the flow is finished) from a payload merely being refused (the caller backs off);
417// it may be NULL.
418#define netflow_filterDatagramEncode(self, q, data, len, out, fatalp) NetFlow__filterDatagramEncode(NetFlow(self), NetQueue(q), data, len, out, fatalp)
419
420bool NetFlow__filterDatagramSend(_In_ NetFlow* self, _In_opt_ NetQueue* q, _Inout_ NetSocket* sock, _In_reads_bytes_opt_(len) const uint8* data, size_t len);
421// bool netflow_filterDatagramSend(NetFlow* self, NetQueue* q, NetSocket* sock, const uint8* data, size_t len);
422//
423// Datagram send: queue `data` (NULL/0 for a priming pass) on the flow's staging queue, run
424// the encode chain repeatedly until no stage has more to produce, and send every message the
425// wire-end stage produced to the flow's peer. Returns false if the payload could not be
426// accepted (staging queue full, pool exhausted) or a stage failed fatally.
427#define netflow_filterDatagramSend(self, q, sock, data, len) NetFlow__filterDatagramSend(NetFlow(self), NetQueue(q), NetSocket(sock), data, len)
428
429bool NetFlow__filterDatagramRecv(_In_ NetFlow* self, _In_opt_ NetQueue* q, _Inout_ NetSocket* sock, _Inout_ NetMessage* msg, _Inout_ NetMsgQueue* out);
430// bool netflow_filterDatagramRecv(NetFlow* self, NetQueue* q, NetSocket* sock, NetMessage* msg, NetMsgQueue* out);
431//
432// Datagram recv: run one received message through the decode chain (consuming it), send any
433// wire-bound output the pass produced, and append whatever came out the application end to
434// `out` for the caller to deliver. Runs on a worker (drainFlow). Returns false on a fatal
435// transform error.
436#define netflow_filterDatagramRecv(self, q, sock, msg, out) NetFlow__filterDatagramRecv(NetFlow(self), NetQueue(q), NetSocket(sock), msg, out)
437
438void NetFlow__primeFilters(_In_ NetFlow* self, _In_opt_ NetQueue* q, _Inout_ NetSocket* sock);
439// void netflow_primeFilters(NetFlow* self, NetQueue* q, NetSocket* sock);
440//
441// Run the encode side once with nothing staged, so a filter that initiates a negotiation gets
442// to emit its first flight. Called when a chain is built (datagram), when the transport comes
443// up (stream), and after shutdown() to flush a close record. A no-op for an unfiltered flow,
444// or a stream flow whose socket is not connected yet.
445#define netflow_primeFilters(self, q, sock) NetFlow__primeFilters(NetFlow(self), NetQueue(q), NetSocket(sock))
446
447//
448// needed to init lastActive
461#define netflowClose(self) (self)->_->close(NetFlow(self))
475#define netflowSend(self, data, len, flags) (self)->_->send(NetFlow(self), data, len, flags)
476
477CX_C_END
#define saDeclarePtr(name)
Definition sarray.h:100
NetCloseReason
Definition net_shared.h:166
uint64 NetTimerId
Handle to an armed timer, unique for the lifetime of its queue.
Definition net_shared.h:210
#define _objfactory_guaranteed
Definition objimpl.h:106
CX Struct System - Introspectable, serializable POD structures in C.
CX Object System - Object-oriented programming in C.
Definition mutex.h:60
Socket-level factory for per-flow filters.
Definition filter.h:184
A single ordering domain: one connection, or one datagram peer.
Definition flow.h:116
sa_stvar user
Application state for this peer, allocated lazily.
Definition flow.h:164
atomic(uint32) lastActive
Last time a packet was ingested for this flow, for approximate LRU.
NetMsgQueue encInMsgs
Datagram: staging queue the encode chain consumes from, the encIn of the message side.
Definition flow.h:190
NetAddr peer
Definition flow.h:139
uint64 key
QUIC: the stream id this flow is keyed on. Zero on every other kind of flow.
Definition flow.h:144
sa_uint64 timers
Ids of the timers currently armed on this flow, guarded by the queue's timerLock.
Definition flow.h:171
void * handlerCtx
Definition flow.h:166
NetPool * pool
Pool every message on this flow was drawn from, held strongly.
Definition flow.h:138
Weak(ObjInst) *handlerWeak
Context passed to per-flow handlers, set by setHandlersObj() – NULL when handlerCtx is in use instead...
NetMessage * ready
Consumer-private FIFO head, refilled from inbox.
Definition flow.h:152
NetMessage * readytail
Definition flow.h:153
sa_NetFlowFilter filters
This flow's filter chain, ordered application -> wire, or empty for none.
Definition flow.h:177
Mutex filterLock
Serializes filter chain access for this flow.
Definition flow.h:197
atomic(uint32) claimed
A worker is currently draining this flow.
BufRing * encIn
Stream: staging ring the encode chain consumes from, allocated with the chain.
Definition flow.h:184
atomic(uint32) queued
Present on the queue's runqueue.
atomic(uint32) dying
atomic(ptr) inbox
Pending messages for this flow, in an intrusive lock-free stack.
uint8 closeReason
NetCloseReason once the flow is dying.
Definition flow.h:198
NetHandlers * handlers
Per-flow handler overrides, optional.
Definition flow.h:165
RWLock handlerLock
Guards handlers/handlerCtx/handlerWeak against a concurrent setHandlers()/setHandlersObj()
Definition flow.h:170
Weak(NetSocket) *socket
Socket that owns this flow.
Set of event handlers, registered per flow, per socket, or queue-wide.
Definition net_shared.h:678
A received packet, as delivered to a handler.
Definition net_shared.h:361
Simple intrusive FIFO of NetMessage, linked through NetMessage::next.
Definition net_shared.h:416
Shared, capped pool of network message buffers and headers.
Definition pool.h:51
NetQueue manages one or more sockets and a thread pool of workers.
Definition queue.h:79