![]() |
Nilorea Library
C utilities for networking, threading, graphics
|
Single-threaded epoll reactor. More...
#include "nilorea/n_reactor.h"#include "nilorea/n_log.h"#include "nilorea/n_common.h"#include "nilorea/n_str.h"#include "nilorea/n_list.h"#include "nilorea/n_network.h"#include "nilorea/n_zlib.h"#include "nilorea/n_lz4.h"#include <stdlib.h>#include <string.h>#include <errno.h>#include <fcntl.h>
Include dependency graph for n_reactor.c:Go to the source code of this file.
Functions | |
| void | n_reactor_close_netw_sync (NETWORK *netw) |
| Synchronously close a reactor-registered NETWORK from the game thread. | |
| void | n_reactor_destroy (n_reactor **reactor) |
| Tear down a reactor. | |
| void | n_reactor_get_stats (const n_reactor *reactor, n_reactor_stats *out) |
Read current stats counters into *out. | |
| n_reactor * | n_reactor_new (int max_fds_hint) |
| Create a new reactor. | |
| void | n_reactor_notify_send (NETWORK *netw) |
| Producer-side wake-up after a netw_add_msg. | |
| int | n_reactor_register (n_reactor *reactor, NETWORK *netw) |
| Register a NETWORK with the reactor. | |
| void | n_reactor_run (n_reactor *reactor) |
| Run the epoll loop on the calling thread. | |
| void * | n_reactor_run_thread_entry (void *arg) |
pthread_create-compatible entry point that calls n_reactor_run on the reactor passed via arg. | |
| void | n_reactor_stop (n_reactor *reactor) |
| Signal the run loop to exit at the next iteration. | |
| void | n_reactor_unregister (n_reactor *reactor, NETWORK *netw) |
| Unregister a NETWORK from the reactor. | |
| NETWORK * | netw_accept_into_reactor (NETWORK *listener, size_t send_list_limit, size_t recv_list_limit, int blocking, n_reactor *reactor, int *retval) |
Accept a connection on listener and register it with reactor instead of starting per-connection threads. | |
Single-threaded epoll reactor.
See nilorea/n_reactor.h for the design rationale, the public API contract, and the cross-platform availability gate.
Definition in file n_reactor.c.
| void n_reactor_close_netw_sync | ( | NETWORK * | netw | ) |
Synchronously close a reactor-registered NETWORK from the game thread.
Sets NETW_EXIT_ASKED on the NETWORK, wakes the reactor, then blocks until the reactor has drained any pending sends (best effort), called shutdown(SHUT_WR) on the socket so the peer sees EOF, and removed the fd from the epoll set. After this returns the caller may safely close() the socket, the reactor will not touch it again.
Idempotent / safe variants:
netw: returns immediately.netw->reactor_handle already cleared (race with reactor-initiated unregister): returns immediately.Must NOT be called from the reactor thread itself, that would self-deadlock waiting for the very ack it should be producing. The Nilorea server calls it from the game thread (netw_close indirectly) and from the accept-pool worker thread (during orderly shutdown), both of which are separate from the reactor thread.
Bounded latency: typically one epoll iteration (<=1 ms with the heartbeat sweep timeout). Worst case is bounded by n_reactor_run's 1-second heartbeat timeout when the reactor thread happens to be wedged in something it can't preempt.
Definition at line 1252 of file n_reactor.c.
References netw.
Referenced by netw_close().
Here is the caller graph for this function:| void n_reactor_destroy | ( | n_reactor ** | reactor | ) |
Tear down a reactor.
Stops the run loop if it's running, drains pending events, closes the epoll + eventfd, frees per-NETWORK state. The caller is responsible for cleaning up the NETWORK * instances themselves (via netw_close); this only frees the reactor's internal bookkeeping.
Sets *reactor = NULL on return. Safe to call with *reactor already NULL.
Definition at line 1215 of file n_reactor.c.
Referenced by run_server().
Here is the caller graph for this function:| void n_reactor_get_stats | ( | const n_reactor * | reactor, |
| n_reactor_stats * | out | ||
| ) |
Read current stats counters into *out.
Thread-safe. Atomic reads, values are consistent for individual fields but the snapshot across fields is not strictly synchronised (no mutex). Useful for periodic dashboard logging where field-level skew is irrelevant.
Definition at line 1232 of file n_reactor.c.
Referenced by run_server().
Here is the caller graph for this function:| n_reactor * n_reactor_new | ( | int | max_fds_hint | ) |
Create a new reactor.
Allocates the epoll fd, the wake-up eventfd, and zero-inits the per-NETWORK state slots. Does NOT start a thread, caller decides whether to put n_reactor_run on its own thread or run it inline.
| max_fds_hint | expected max number of registered NETWORKs. Used to size internal tables; can be exceeded at the cost of a realloc. Pass 0 to use the default (256). |
Definition at line 1207 of file n_reactor.c.
References LOG_INFO, and n_log.
Referenced by run_server().
Here is the caller graph for this function:| void n_reactor_notify_send | ( | NETWORK * | netw | ) |
Producer-side wake-up after a netw_add_msg.
Called by netw_add_msg after pushing onto netw->send_buf when netw->reactor_mode == 1. Writes a byte to the reactor's wake eventfd so the run loop pulls out of epoll_wait and scans for newly-ready sends. Cheap (one 8-byte syscall, no locking).
Safe to call from any thread. No-op if netw->reactor_handle is NULL (NETWORK not registered, or already unregistered).
Definition at line 1248 of file n_reactor.c.
References netw.
Referenced by netw_add_msg().
Here is the caller graph for this function:Register a NETWORK with the reactor.
The NETWORK must NOT have had netw_start_thr_engine called on it. The socket is set O_NONBLOCK, added to the reactor's epoll set with EPOLLIN | EPOLLET, and netw->reactor_mode is set to 1.
From this point on, raw socket reads happen on the reactor's thread; complete N_STR frames are pushed into netw->recv_buf exactly as the thread-mode netw_recv_func would. The game thread keeps using netw_get_msg unchanged.
Returns 1 on success, 0 on any failure (registration aborted, NETWORK left untouched, caller can fall back to thread engine).
Definition at line 1237 of file n_reactor.c.
References netw.
| void n_reactor_run | ( | n_reactor * | reactor | ) |
Run the epoll loop on the calling thread.
Blocks until n_reactor_stop is called. Drains pending events once after stop is signalled, then returns. Idempotent, calling twice in a row from the same thread is safe but pointless; calling from two threads simultaneously is undefined.
Definition at line 1219 of file n_reactor.c.
| void * n_reactor_run_thread_entry | ( | void * | arg | ) |
pthread_create-compatible entry point that calls n_reactor_run on the reactor passed via arg.
Convenience for callers that want to put the reactor on a dedicated thread without writing their own one-line adapter. Returns NULL.
Definition at line 1227 of file n_reactor.c.
Referenced by run_server().
Here is the caller graph for this function:| void n_reactor_stop | ( | n_reactor * | reactor | ) |
Signal the run loop to exit at the next iteration.
Thread-safe, may be called from any thread. The call writes to a dedicated stop-eventfd registered with the epoll set; the run loop observes the wakeup and breaks out. n_reactor_run returns shortly after.
Definition at line 1223 of file n_reactor.c.
Referenced by run_server().
Here is the caller graph for this function:Unregister a NETWORK from the reactor.
Removes the socket from the epoll set and frees any per-connection recv accumulator state. Does NOT close the socket, the caller (netw_close / netw_close_ex) handles the actual close. Safe to call on a NETWORK that was never registered (no-op).
Definition at line 1243 of file n_reactor.c.
References netw.
| NETWORK * netw_accept_into_reactor | ( | NETWORK * | listener, |
| size_t | send_list_limit, | ||
| size_t | recv_list_limit, | ||
| int | blocking, | ||
| n_reactor * | reactor, | ||
| int * | retval | ||
| ) |
Accept a connection on listener and register it with reactor instead of starting per-connection threads.
Thin wrapper around netw_accept_from_ex: passes the blocking parameter through (semantics identical, see that function's docs, 0 = blocking accept, > 0 = millisec select timeout, -1 = non-blocking accept), then calls n_reactor_register on the accepted NETWORK. On any failure (accept returned NULL, or register rejected the NETWORK) the accepted socket is closed and NULL is returned, so the caller doesn't have to track partial state.
Use this from the listener loop when the server is configured for reactor mode. The plain netw_accept_from_ex + netw_start_thr_engine path stays available unchanged for thread-mode listeners, both modes can coexist in the same process (one listener per port, each with its own choice).
Returns the accepted NETWORK on success, NULL otherwise. retval is forwarded from the underlying accept (errno on failure, 0 on success) to mirror netw_accept_from_ex's contract.
Definition at line 1256 of file n_reactor.c.
Referenced by run_server().
Here is the caller graph for this function: