Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_reactor.h File Reference

Single-threaded epoll reactor for n_network connections. More...

+ Include dependency graph for n_reactor.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  n_reactor_stats
 Counters for the dashboard / profile_server.sh. More...
 

Macros

#define N_REACTOR_AVAILABLE   0
 

Typedefs

typedef struct n_reactor n_reactor
 Opaque reactor handle.
 

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_reactorn_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.
 
NETWORKnetw_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.
 

Detailed Description

Single-threaded epoll reactor for n_network connections.

Alternative to the per-connection thread engine for n_network sockets. One reactor multiplexes thousands of connections on a single epoll fd; the per-NETWORK send/recv queues stay exactly as with the thread engine (game thread keeps using netw_add_msg / netw_get_msg unchanged), only the underlying I/O is consolidated.

Strictly additive. The existing thread engine (netw_start_thr_engine, netw_send_func, netw_recv_func) is unchanged and remains the default. Reactor mode is opt-in per NETWORK * instance via n_reactor_register.

Linux + Android only. Guarded by __linux__ || __ANDROID__; on every other platform the public functions are still declared (so callers don't need their own #ifdefs) but n_reactor_new returns NULL with a LOG_INFO and the rest are no-ops returning error codes. Server config validators must clamp network_mode back to "threads" when the reactor isn't available.

Definition in file n_reactor.h.


Data Structure Documentation

◆ n_reactor_stats

struct n_reactor_stats

Counters for the dashboard / profile_server.sh.

Cheap atomic loads, safe to call from any thread.

Examples
ex_network_reactor.c.

Definition at line 78 of file n_reactor.h.

+ Collaboration diagram for n_reactor_stats:
Data Fields
long long events_processed total epoll events dispatched
long long fds_registered lifetime register call count
long long fds_unregistered lifetime unregister call count
long long reads_partial EAGAIN on recv -> kept accumulator.
long long wake_signals eventfd wake events processed
long long wake_walk_drains
long long wake_walk_visits
long long wake_walks
long long writes_partial EAGAIN on send -> re-armed EPOLLOUT.

Macro Definition Documentation

◆ N_REACTOR_AVAILABLE

#define N_REACTOR_AVAILABLE   0

Definition at line 61 of file n_reactor.h.

Typedef Documentation

◆ n_reactor

typedef struct n_reactor n_reactor

Opaque reactor handle.

Allocated by n_reactor_new, released by n_reactor_destroy.

Definition at line 74 of file n_reactor.h.

Function Documentation

◆ n_reactor_close_netw_sync()

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:

  • NULL or non-reactor-mode 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:

◆ n_reactor_destroy()

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:

◆ n_reactor_get_stats()

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_new()

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.

Parameters
max_fds_hintexpected 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).
Returns
reactor pointer on success, NULL on allocation failure OR on platforms where N_REACTOR_AVAILABLE == 0.

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:

◆ n_reactor_notify_send()

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:

◆ n_reactor_register()

int n_reactor_register ( n_reactor reactor,
NETWORK netw 
)

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.

◆ n_reactor_run()

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.

◆ n_reactor_run_thread_entry()

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:

◆ n_reactor_stop()

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:

◆ n_reactor_unregister()

void n_reactor_unregister ( n_reactor reactor,
NETWORK netw 
)

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.

◆ netw_accept_into_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.

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: