|
CX Framework
Cross-platform C utility framework
|
Data Structures | |
| struct | NetQueue |
| NetQueue manages one or more sockets and a thread pool of workers. More... | |
Macros | |
| #define | netqueuePresetClient(conf) NetQueue_presetClient(conf) |
| #define | netqueuePresetServer(conf) NetQueue_presetServer(conf) |
| #define | netqueueConnect(self, host, port, handlers, ctx) NetQueue_connect(NetQueue(self), host, port, handlers, ctx) |
| #define | netqueueConnectPrep(self, host, port, handlers, ctx, prep, prepctx) NetQueue_connectPrep(NetQueue(self), host, port, handlers, ctx, prep, prepctx) |
| #define | netqueueListen(self, addr, backlog, handlers, ctx) NetQueue_listen(NetQueue(self), addr, backlog, handlers, ctx) |
| #define | netqueueSetHandlers(self, handlers, ctx) NetQueue_setHandlers(NetQueue(self), handlers, ctx) |
| #define | netqueueSetHandlersObj(self, handlers, ctx) NetQueue_setHandlersObj(NetQueue(self), handlers, ObjInst(ctx)) |
| #define | netqueuePromoteFlow(self, sock, peer) NetQueue_promoteFlow(NetQueue(self), sock, peer) |
| #define | netqueueDroppedNoBuf(self) NetQueue_droppedNoBuf(NetQueue(self)) |
| #define | netqueueAddSocket(self, socket) (self)->_->addSocket(NetQueue(self), socket) |
| #define | netqueueRemoveSocket(self, socket) (self)->_->removeSocket(NetQueue(self), socket) |
| #define | netqueueSocket(self, type) (self)->_->socket(NetQueue(self), type) |
| #define | netqueueShutdown(self, timeout) (self)->_->shutdown(NetQueue(self), timeout) |
| #define | netqueueTick(self, wait) (self)->_->tick(NetQueue(self), wait) |
Typedefs | |
| typedef struct NetQueue | NetQueue |
| NetQueue manages one or more sockets and a thread pool of workers. | |
Functions | |
| NetQueue * | netqueueCreate (const NetQueueConfig *conf) |
NetQueue is an abstraction for servicing one or more sockets using platform-specific backends. Sockets can be added to a queue to handle I/O operations efficiently. The queue can be operated in single-threaded polled mode or with a pool of worker threads for asynchronous operation.
| #define netqueueAddSocket | ( | self, | |
| socket | |||
| ) | (self)->_->addSocket(NetQueue(self), socket) |
bool netqueueAddSocket(NetQueue* self, NetSocket* socket);
Add a socket to be managed by the queue
The socket must be compatible with the NetQueue implementation. The netqueueSocket() factory can be used to create compatible sockets.
| socket | Socket to add to the queue |
| #define netqueueConnect | ( | self, | |
| host, | |||
| port, | |||
| handlers, | |||
| ctx | |||
| ) | NetQueue_connect(NetQueue(self), host, port, handlers, ctx) |
NetSocket* netqueueConnect(NetQueue* self, strref host, uint16 port, const NetHandlers* handlers, void* ctx);
Create a stream socket on the queue and start connecting it, in one call
Composes the whole client setup – netqueueSocket(), netqueueAddSocket(), handler registration, and netsocketConnect() – so a single outbound connection is one call instead of four. Handlers are registered before the connect begins, so the NET_Connection event cannot slip past. Purely a convenience over the public API; use the individual calls when the socket needs configuration between the steps.
| host | Hostname or literal address to connect to (NULL/empty means loopback) |
| port | Port number, host byte order |
| handlers | Per-socket handler overrides, or NULL for none. Not copied – must outlive the socket, so it is usually static const |
| ctx | Context passed to these handlers on NetEvent.ctx |
Example:
| #define netqueueConnectPrep | ( | self, | |
| host, | |||
| port, | |||
| handlers, | |||
| ctx, | |||
| prep, | |||
| prepctx | |||
| ) | NetQueue_connectPrep(NetQueue(self), host, port, handlers, ctx, prep, prepctx) |
Create a stream socket, hand it over, and start connecting it, in one call
netqueueConnect() with one addition: prep is called with the finished socket immediately before the connect begins. Use it when the socket has to be reachable from somewhere else – a request, a session, a cancel path – before its first event can arrive, which the return value is too late for: the connect can complete on another thread while this call is still returning.
A NULL return means nothing was started. Anything prep stored is the caller's to clean up.
| host | Hostname or literal address to connect to (NULL/empty means loopback) |
| port | Port number, host byte order |
| handlers | Per-socket handler overrides, or NULL for none. Not copied – must outlive the socket, so it is usually static const |
| ctx | Context passed to these handlers on NetEvent.ctx |
| prep | Called with the socket just before it connects, or NULL for none |
| prepctx | Context passed to prep |
Example:
| #define netqueueDroppedNoBuf | ( | self | ) | NetQueue_droppedNoBuf(NetQueue(self)) |
uint32 netqueueDroppedNoBuf(NetQueue* self);
Datagrams dropped for lack of a receive buffer
A silent drop is indistinguishable from a network problem. A counter that is nonzero turns "the network is flaky" into "the pool is too small or a callback is too slow" immediately, so this is worth logging at whatever cadence the application already has.
| #define netqueueListen | ( | self, | |
| addr, | |||
| backlog, | |||
| handlers, | |||
| ctx | |||
| ) | NetQueue_listen(NetQueue(self), addr, backlog, handlers, ctx) |
NetSocket* netqueueListen(NetQueue* self, const NetAddr* addr, int backlog, const NetHandlers* handlers, void* ctx);
Create a stream socket on the queue and start it listening, in one call
The accept-side counterpart of netqueueConnect(): composes netqueueSocket(), netqueueAddSocket(), handler registration, netsocketBind(), and netsocketListen(). Incoming connections arrive as NET_Accepted events on the listener; with NQ_AutoAccept set on the queue they are registered with it automatically.
| addr | Local address to bind (port 0 lets the OS pick one) |
| backlog | Listen backlog, or 0 for a platform-specific default |
| handlers | Per-socket handler overrides, or NULL for none. Not copied – must outlive the socket, so it is usually static const |
| ctx | Context passed to these handlers on NetEvent.ctx |
Example:
| #define netqueuePresetClient | ( | conf | ) | NetQueue_presetClient(conf) |
void netqueuePresetClient(NetQueueConfig* conf);
Fill in a configuration suited to a client or a single connection
Polled mode, a small buffer pool, and few flows. Nothing here starts a thread, so a program that drives its own loop with netqueueTick() never spawns one.
| conf | Configuration structure to populate |
| #define netqueuePresetServer | ( | conf | ) | NetQueue_presetServer(conf) |
void netqueuePresetServer(NetQueueConfig* conf);
Fill in a configuration suited to a server
A worker pool sized to the machine, a large buffer pool, and a high flow cap. Suitable as-is for a process multiplexing many peers over a single datagram socket.
| conf | Configuration structure to populate |
| #define netqueuePromoteFlow | ( | self, | |
| sock, | |||
| peer | |||
| ) | NetQueue_promoteFlow(NetQueue(self), sock, peer) |
NetFlow* netqueuePromoteFlow(NetQueue* self, NetSocket* sock, const NetAddr* peer);
Admit a peer that was refused by the flow cap
Called from a NET_FlowRefused handler once the application has validated the packet – completed a handshake, checked a crypto negotiation, whatever its protocol requires. This is what makes a public datagram port safe: past the cap the queue stops allocating on its own and hands raw packets to code that already knows how to tell a real client from a flood. The admitted flow fires NET_FlowOpen like any other, ordered ahead of its first packet.
| sock | Socket the packet arrived on |
| peer | Source address to admit |
| #define netqueueRemoveSocket | ( | self, | |
| socket | |||
| ) | (self)->_->removeSocket(NetQueue(self), socket) |
bool netqueueRemoveSocket(NetQueue* self, NetSocket* socket);
Remove a socket from the queue, usually when the socket is closed
For some implementations this will also attempt to cancel pending I/O requests that reference the socket's owned buffers.
| socket | Socket to remove from the queue |
| #define netqueueSetHandlers | ( | self, | |
| handlers, | |||
| ctx | |||
| ) | NetQueue_setHandlers(NetQueue(self), handlers, ctx) |
void netqueueSetHandlers(NetQueue* self, const NetHandlers* handlers, void* ctx);
Register the queue-wide fallback handlers
This is the last level of the per-field fallthrough described in Event Handlers, and is generally the right place for logging and error handling. The handler struct is not copied – it must outlive the queue, which is why it is usually static const.
| handlers | Handler set, or NULL to clear |
| ctx | Context passed to these handlers on NetEvent.ctx |
| #define netqueueSetHandlersObj | ( | self, | |
| handlers, | |||
| ctx | |||
| ) | NetQueue_setHandlersObj(NetQueue(self), handlers, ObjInst(ctx)) |
void netqueueSetHandlersObj(NetQueue* self, const NetHandlers* handlers, ObjInst* ctx);
Register the queue-wide fallback handlers, with an object as the context
Same as setHandlers(), except ctx is held weakly rather than borrowed.
| handlers | Handler set, or NULL to clear |
| ctx | Object passed to these handlers on NetEvent.ctx, held weakly; NULL to clear |
| #define netqueueShutdown | ( | self, | |
| timeout | |||
| ) | (self)->_->shutdown(NetQueue(self), timeout) |
bool netqueueShutdown(NetQueue* self, int64 timeout);
Shut down the queue
Removes all sockets from the queue and shuts down worker threads. Will shut down the queue in the background even if false is returned.
| timeout | How long to wait for workers to terminate, in microseconds (see timeS() / timeMS()), or 0 or less to wait forever |
| #define netqueueSocket | ( | self, | |
| type | |||
| ) | (self)->_->socket(NetQueue(self), type) |
NetSocket* netqueueSocket(NetQueue* self, NetSocketType type);
Factory for creating sockets
This does NOT add the socket to the queue.
| type | Type of socket to create (connection-oriented or connectionless) |
| #define netqueueTick | ( | self, | |
| wait | |||
| ) | (self)->_->tick(NetQueue(self), wait) |
bool netqueueTick(NetQueue* self, int64 wait);
Process events in polled mode
Only for a queue created with nthreads set to 0. A queue with worker threads already does this work on its own threads, and calling tick() on one puts two threads in the same place.
| wait | How long to wait for an event, in microseconds (see timeS() / timeMS()), 0 to return immediately, or timeForever to wait until something happens. A wait of under a millisecond is rounded up to one on some platforms, so treat it as a lower bound. |
| NetQueue * netqueueCreate | ( | const NetQueueConfig * | conf | ) |
Create a network queue
| conf | Creation configuration; start from a preset and override what you need |
Example: