1#include <cx/net/net_shared.h>
2#include <cx/buffer/bufpool.h>
4/// @addtogroup net_pool
6/// The shared, hard-capped storage every NetMessage on a queue is drawn from.
8/// One NetPool backs a whole NetQueue: `recvBufMax * recvBufSize` is the entire receive-side
9/// memory ceiling for every socket on it, and the NetMessage headers that carry those payloads are
10/// recycled alongside them so the steady-state packet path allocates nothing at all.
12/// It is a separate, reference-counted object rather than a member of the queue because the things
13/// that hold messages do not all share the queue's lifetime. A flow, or a datagram filter stage in
14/// the middle of a handshake, can be torn down at a moment when the queue is no longer reachable
15/// from it -- and a pooled buffer destroyed instead of returned shrinks the ceiling permanently,
16/// for every socket sharing the pool. So anything that can be holding a message keeps its own
17/// reference to the pool, and always has somewhere to put the message back.
19/// Every entry point tolerates a NULL pool and falls back to the heap, which is what lets a socket
20/// that was never added to a queue still send: there is no pool to draw on, so there is no ceiling
21/// to enforce and nothing to return the payload to.
23/// @brief Shared, capped pool of network message buffers and headers
25/// Queue-wide storage that anything holding a NetMessage keeps a reference to, so a buffer is
26/// always returned rather than destroyed. See @ref net_pool.
28 /// @brief Pooled receive buffers, capped at queue creation
30 /// recvBufMax * recvBufSize is the entire receive-side memory ceiling. When the pool is dry
31 /// the datagram path drops the packet and bumps droppedNoBuf rather than allocating; the
32 /// stream path instead stops posting receives and lets TCP flow control apply backpressure.
35 /// @brief Free NetMessage headers, so the steady-state packet path allocates nothing
38 /// Allocate a bare NetMessage header, with no payload buffer attached
40 /// The message every internal event rides on: a terminal marker, a connect result, an accepted
41 /// socket, or -- on the stream path -- a "bytes arrived" marker for data that already went into
42 /// the socket's receive ring. Fill in `kind` and whichever payload slot that kind uses. A
43 /// datagram carrying an actual payload wants allocMsg() instead.
45 /// Unlike the buffer pool, the header freelist is only a cache: this falls back to the heap
46 /// when it is empty (or when `self` is NULL), so it never fails and imposes no ceiling of its
49 /// @return A zeroed message header; never NULL
50 unbound NetMessage* allocHeader();
52 /// Allocate a NetMessage with an empty payload buffer, for a filter to fill in
54 /// This is how a datagram filter produces output: a handshake flight of its own, or a fragment
55 /// of an application message too large for the wire. Both the header and the buffer come from
56 /// this pool, so the steady state allocates nothing, and the message is reclaimed correctly
57 /// whichever way it eventually leaves -- delivered to the application, sent, or dropped.
59 /// The returned message has `buf` set to a pool-sized buffer with `len` 0; write the payload
60 /// into `buf->data` and set `buf->len`. The buffer must not be resized or replaced. Append the
61 /// message to the stage's own encOut/decOut with netMsgQueuePush(); do not free it.
63 /// The pool has a hard cap, and hitting it is a normal (if unwelcome) condition: a filter handed
64 /// NULL here should simply produce nothing this pass rather than reaching for an allocation
65 /// that would defeat the ceiling the pool exists to enforce.
67 /// @return A message with an empty pooled buffer, or NULL if the pool is at its cap (or `self`
68 /// is NULL, which has no buffers to hand out)
69 [sal _Check_return_ _Ret_maybenull_] unbound NetMessage* allocMsg();
71 /// Retire a NetMessage, returning its header and payload to the pool
73 /// Where the payload goes travels with the message rather than being inferred from which path
74 /// frees it: a packet from the wire and a filter's own output came from a pool, while an
75 /// oversized send payload is a plain heap buffer. Getting that wrong is not a crash but a slow
76 /// leak of pool capacity, so the message records it and this is the only place that acts on it.
78 /// @param msg The message to retire; set to NULL on return
79 [sal _At_(*msg, _Pre_notnull_ _Post_null_)] unbound void freeMsg([inout] NetMessage** msg);
81 /// Retire every message left in a queue, leaving it empty
83 /// What a datagram stage calls on its own encOut/decOut when it is torn down with output still
84 /// staged: nobody is going to drain those messages now, and dropping them on the floor would
85 /// cost the pool their buffers for good.
87 /// @param mq The message queue to drain
88 unbound void freeMsgQueue([inout] NetMsgQueue *mq);
90 /// Create the pool a queue's messages will be drawn from
92 /// Called by NetQueue's init once the config has been applied, since the recvBuf* settings are
93 /// what size the buffer pool and fix its cap.
95 /// @param conf The owning queue's configuration
96 factory create([in] const NetQueueConfig *conf);