43#if N_REACTOR_AVAILABLE
48#include <sys/eventfd.h>
49#include <sys/socket.h>
56#define N_REACTOR_DEFAULT_MAX_FDS 256
61#define N_REACTOR_BATCH_SIZE 64
77 pthread_mutex_t registered_lock;
81 atomic_llong events_processed;
82 atomic_llong writes_partial;
83 atomic_llong reads_partial;
84 atomic_llong fds_registered;
85 atomic_llong fds_unregistered;
86 atomic_llong wake_signals;
90 atomic_llong wake_walks;
91 atomic_llong wake_walk_visits;
92 atomic_llong wake_walk_drains;
99 pthread_mutex_t dirty_lock;
104static int register_internal_efd(
int epoll_fd,
int efd, uint64_t tag) {
109 struct epoll_event ev;
112 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, efd, &ev) != 0) {
113 n_log(
LOG_ERR,
"n_reactor: epoll_ctl ADD eventfd failed: %s",
125#define N_REACTOR_TAG_STOP 1ULL
126#define N_REACTOR_TAG_WAKE 2ULL
127#define N_REACTOR_TAG_INTERNAL_MAX 16ULL
132static long long drain_eventfd(
int efd) {
136 ssize_t n = read(efd, &buf,
sizeof(buf));
137 if (n ==
sizeof(buf)) {
138 total += (
long long)buf;
141 if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
break;
151static int set_nonblocking(
int fd) {
152 int flags = fcntl(fd, F_GETFL, 0);
153 if (flags < 0)
return 0;
154 if ((flags & O_NONBLOCK) == O_NONBLOCK)
return 1;
155 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
return 0;
160static void reactor_recv_state_reset(
NETWORK*
netw) {
162 netw->reactor_read_phase = 0;
163 netw->reactor_read_hdr_have = 0;
164 netw->reactor_read_pkt_state = 0;
165 netw->reactor_read_pkt_length = 0;
166 netw->reactor_read_payload_have = 0;
170static void reactor_send_state_reset(
NETWORK*
netw) {
172 netw->reactor_send_len = 0;
173 netw->reactor_send_off = 0;
174 netw->reactor_write_armed = 0;
175 netw->reactor_send_wants_read = 0;
176 netw->reactor_recv_wants_write = 0;
183 struct epoll_event ev;
186 return epoll_ctl(r->epoll_fd, EPOLL_CTL_MOD,
netw->link.sock, &ev) == 0;
200static int reactor_send_queue_nonempty(
NETWORK*
netw) {
201 if (!
netw->send_buf)
return 0;
202 pthread_mutex_lock(&
netw->sendbolt);
203 int nonempty = (
netw->send_buf->nb_items > 0);
204 pthread_mutex_unlock(&
netw->sendbolt);
218static int reactor_send_state_load_next(
NETWORK*
netw) {
219 if (
netw->reactor_send_buf)
return 1;
221 pthread_mutex_lock(&
netw->sendbolt);
223 pthread_mutex_unlock(&
netw->sendbolt);
234 N_STR* zipped = NULL;
235 uint32_t flag_bit = 0;
243 if (zipped && zipped->
written > 0 &&
249 pkt_state |= flag_bit;
255 if (msg->
written > UINT32_MAX) {
256 n_log(
LOG_ERR,
"n_reactor: payload too large (%zu > UINT32_MAX); dropping",
261 size_t total = 8 + msg->
written;
268 uint32_t state_be = htonl(pkt_state);
269 uint32_t length_be = htonl((uint32_t)msg->
written);
270 memcpy(buf, &state_be, 4);
271 memcpy(buf + 4, &length_be, 4);
275 netw->reactor_send_buf = buf;
276 netw->reactor_send_len = total;
277 netw->reactor_send_off = 0;
291 if (!
netw->reactor_send_buf) {
292 int r = reactor_send_state_load_next(
netw);
293 if (r < 0)
return -1;
294 if (r == 0)
return 1;
296 size_t remaining =
netw->reactor_send_len -
netw->reactor_send_off;
297 if (remaining == 0) {
300 netw->reactor_send_buf = NULL;
301 netw->reactor_send_len = 0;
302 netw->reactor_send_off = 0;
310 netw->reactor_send_wants_read = 0;
311 uint32_t attempt = (remaining > UINT32_MAX) ? UINT32_MAX : (uint32_t)remaining;
312 ssize_t sent =
netw->send_data_once((
void*)
netw,
313 netw->reactor_send_buf +
netw->reactor_send_off,
316 netw->reactor_send_off += (size_t)sent;
320 atomic_fetch_add(&reactor->writes_partial, 1);
328 netw->reactor_send_wants_read = 1;
329 atomic_fetch_add(&reactor->writes_partial, 1);
342static void reactor_recv_dispatch_frame(
NETWORK*
netw,
354 msg->
data = pkt_payload;
355 msg->
length = (size_t)pkt_length + 1;
356 msg->
written = (size_t)pkt_length;
361 msg->
data[pkt_length] =
'\0';
366 if (want_zlib || want_lz4) {
373 "n_reactor: failed to decompress payload "
374 "(%" PRIu32
" bytes, codec=%s); dropping",
375 pkt_length, want_lz4 ?
"lz4" :
"zlib");
381 pthread_mutex_lock(&
netw->recvbolt);
383 pthread_mutex_unlock(&
netw->recvbolt);
384 n_log(
LOG_ERR,
"n_reactor: recv_buf list_push failed; dropping frame");
388 pthread_mutex_unlock(&
netw->recvbolt);
403static void reactor_sweep_exit_asked(
n_reactor* reactor) {
404 NETWORK* batch[N_REACTOR_BATCH_SIZE];
407 pthread_mutex_lock(&reactor->registered_lock);
408 LIST_NODE* node = reactor->registered->start;
409 while (node && count < N_REACTOR_BATCH_SIZE) {
420 pthread_mutex_unlock(&reactor->registered_lock);
422 for (
int i = 0; i < count; i++) {
428 (void)reactor_drain_writes(n, reactor);
457 netw->reactor_recv_wants_write = 0;
460 ssize_t got =
netw->recv_data_once((
void*)
netw, chunk, (uint32_t)
sizeof(chunk));
470 netw->reactor_recv_wants_write = 1;
479 const char* p = chunk;
480 size_t rem = (size_t)got;
482 switch (
netw->reactor_read_phase) {
486 size_t need = 4 - (size_t)
netw->reactor_read_hdr_have;
487 size_t take = (rem < need) ? rem : need;
488 memcpy(
netw->reactor_read_hdr_buf +
netw->reactor_read_hdr_have,
490 netw->reactor_read_hdr_have += (int)take;
493 if (
netw->reactor_read_hdr_have < 4) {
495 atomic_fetch_add(&reactor->reads_partial, 1);
500 memcpy(&word,
netw->reactor_read_hdr_buf,
sizeof(word));
502 netw->reactor_read_hdr_have = 0;
503 if (
netw->reactor_read_phase == 0) {
504 netw->reactor_read_pkt_state = word;
511 netw->reactor_read_phase = 1;
520 "n_reactor: rejected oversized frame (%u bytes) exceeding NETW_MSG_MAX_FRAME_SIZE (%lu)",
524 netw->reactor_read_pkt_length = word;
533 n_log(
LOG_ERR,
"n_reactor: alloc(empty payload) failed");
536 reactor_recv_dispatch_frame(
netw,
537 netw->reactor_read_pkt_state,
539 netw->reactor_read_phase = 0;
541 Malloc(
netw->reactor_read_payload,
char, word + 1);
542 if (!
netw->reactor_read_payload) {
543 n_log(
LOG_ERR,
"n_reactor: alloc(%u-byte payload) failed",
547 netw->reactor_read_payload_have = 0;
548 netw->reactor_read_phase = 2;
555 size_t need = (size_t)
netw->reactor_read_pkt_length -
netw->reactor_read_payload_have;
556 size_t take = (rem < need) ? rem : need;
557 memcpy(
netw->reactor_read_payload +
netw->reactor_read_payload_have,
559 netw->reactor_read_payload_have += take;
562 if (
netw->reactor_read_payload_have <
netw->reactor_read_pkt_length) {
565 atomic_fetch_add(&reactor->reads_partial, 1);
570 char* payload =
netw->reactor_read_payload;
571 netw->reactor_read_payload = NULL;
572 reactor_recv_dispatch_frame(
netw,
573 netw->reactor_read_pkt_state,
574 netw->reactor_read_pkt_length,
576 netw->reactor_read_phase = 0;
577 netw->reactor_read_payload_have = 0;
601 r->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
602 if (r->epoll_fd < 0) {
603 n_log(
LOG_ERR,
"n_reactor_new: epoll_create1 failed: %s",
612 r->stop_efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
613 if (r->stop_efd < 0) {
614 n_log(
LOG_ERR,
"n_reactor_new: eventfd(stop) failed: %s",
618 r->wake_efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
619 if (r->wake_efd < 0) {
620 n_log(
LOG_ERR,
"n_reactor_new: eventfd(wake) failed: %s",
625 if (!register_internal_efd(r->epoll_fd, r->stop_efd, N_REACTOR_TAG_STOP) ||
626 !register_internal_efd(r->epoll_fd, r->wake_efd, N_REACTOR_TAG_WAKE)) {
631 if (!r->registered) {
632 n_log(
LOG_ERR,
"n_reactor_new: cannot allocate registered list");
635 pthread_mutex_init(&r->registered_lock, NULL);
637 atomic_store(&r->events_processed, 0);
638 atomic_store(&r->writes_partial, 0);
639 atomic_store(&r->reads_partial, 0);
640 atomic_store(&r->fds_registered, 0);
641 atomic_store(&r->fds_unregistered, 0);
642 atomic_store(&r->wake_signals, 0);
643 atomic_store(&r->wake_walks, 0);
644 atomic_store(&r->wake_walk_visits, 0);
645 atomic_store(&r->wake_walk_drains, 0);
646 r->stop_requested = 0;
651 if (!r->dirty_pending) {
652 n_log(
LOG_ERR,
"n_reactor_new: cannot allocate dirty_pending list");
655 pthread_mutex_init(&r->dirty_lock, NULL);
657 n_log(
LOG_INFO,
"n_reactor_new: created (epoll_fd=%d stop_efd=%d wake_efd=%d)",
658 r->epoll_fd, r->stop_efd, r->wake_efd);
663 if (r->wake_efd >= 0) close(r->wake_efd);
664 if (r->stop_efd >= 0) close(r->stop_efd);
665 if (r->epoll_fd >= 0) close(r->epoll_fd);
672 if (!reactor || !*reactor)
return;
678 if (r->wake_efd >= 0) close(r->wake_efd);
679 if (r->stop_efd >= 0) close(r->stop_efd);
680 if (r->epoll_fd >= 0) close(r->epoll_fd);
683 pthread_mutex_destroy(&r->registered_lock);
685 if (r->dirty_pending) {
687 pthread_mutex_destroy(&r->dirty_lock);
695 if (!reactor)
return;
697 struct epoll_event events[N_REACTOR_BATCH_SIZE];
699 while (!reactor->stop_requested) {
705 int n = epoll_wait(reactor->epoll_fd, events,
706 N_REACTOR_BATCH_SIZE, 1000);
708 if (errno == EINTR)
continue;
717 reactor_sweep_exit_asked(reactor);
721 for (
int i = 0; i < n; i++) {
722 uint64_t tag = events[i].data.u64;
723 atomic_fetch_add(&reactor->events_processed, 1);
725 if (tag == N_REACTOR_TAG_STOP) {
730 drain_eventfd(reactor->stop_efd);
731 reactor->stop_requested = 1;
734 if (tag == N_REACTOR_TAG_WAKE) {
740 long long drained = drain_eventfd(reactor->wake_efd);
741 atomic_fetch_add(&reactor->wake_signals, drained);
748 atomic_fetch_add(&reactor->wake_walks, 1);
749 long long visits_this_walk = 0;
750 long long drains_this_walk = 0;
752 LIST* walk_list = NULL;
754 pthread_mutex_t* walk_lock = NULL;
755 pthread_mutex_lock(&reactor->dirty_lock);
757 LIST* old = reactor->dirty_pending;
760 reactor->dirty_pending = fresh;
767 walk_lock = &reactor->dirty_lock;
771 pthread_mutex_unlock(&reactor->dirty_lock);
787 __atomic_store_n(&
netw->in_dirty_list, 0, __ATOMIC_RELEASE);
794 int has_inflight = (
netw->reactor_send_buf != NULL);
795 int has_queued = reactor_send_queue_nonempty(
netw);
796 if (has_inflight || has_queued) {
798 int rc = reactor_drain_writes(
netw, reactor);
801 if (!
netw->reactor_write_armed) {
802 if (reactor_epoll_mod(reactor,
netw,
803 EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET)) {
804 netw->reactor_write_armed = 1;
818 if (walk_lock) pthread_mutex_unlock(walk_lock);
821 if (walk_lock) pthread_mutex_lock(walk_lock);
824 if (
netw->reactor_write_armed) {
825 if (reactor_epoll_mod(reactor,
netw,
826 EPOLLIN | EPOLLRDHUP | EPOLLET)) {
827 netw->reactor_write_armed = 0;
839 if (walk_lock) pthread_mutex_unlock(walk_lock);
840 if (walk_owned && walk_list) {
845 atomic_fetch_add(&reactor->wake_walk_visits, visits_this_walk);
846 atomic_fetch_add(&reactor->wake_walk_drains, drains_this_walk);
857 uint32_t evmask = events[i].events;
858 if (evmask & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
870 if (evmask & EPOLLIN) {
871 if (!reactor_handle_readable(
netw, reactor)) {
882 if (
netw->reactor_send_wants_read) {
883 int rc = reactor_drain_writes(
netw, reactor);
892 if (rc > 0 &&
netw->reactor_write_armed &&
893 !
netw->reactor_recv_wants_write) {
894 if (reactor_epoll_mod(reactor,
netw,
895 EPOLLIN | EPOLLRDHUP | EPOLLET)) {
896 netw->reactor_write_armed = 0;
898 }
else if (rc == 0 && !
netw->reactor_write_armed) {
899 if (reactor_epoll_mod(reactor,
netw,
900 EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET)) {
901 netw->reactor_write_armed = 1;
908 if (
netw->reactor_recv_wants_write && !
netw->reactor_write_armed) {
909 if (reactor_epoll_mod(reactor,
netw,
910 EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET)) {
911 netw->reactor_write_armed = 1;
915 if (evmask & EPOLLOUT) {
918 if (
netw->reactor_recv_wants_write) {
919 if (!reactor_handle_readable(
netw, reactor)) {
928 int rc = reactor_drain_writes(
netw, reactor);
937 if (rc > 0 &&
netw->reactor_write_armed &&
938 !
netw->reactor_recv_wants_write) {
942 if (reactor_epoll_mod(reactor,
netw,
943 EPOLLIN | EPOLLRDHUP | EPOLLET)) {
944 netw->reactor_write_armed = 0;
954 reactor_sweep_exit_asked(reactor);
958 (
long long)atomic_load(&reactor->events_processed));
962 if (!reactor || reactor->stop_efd < 0)
return;
967 ssize_t w = write(reactor->stop_efd, &one,
sizeof(one));
980 memset(out, 0,
sizeof(*out));
988 out->
wake_signals = atomic_load(&reactor->wake_signals);
989 out->
wake_walks = atomic_load(&reactor->wake_walks);
995 if (!reactor || !
netw)
return 0;
1003 "n_reactor_register: socket %d has thread engine started; "
1004 "reactor and thread mode are mutually exclusive",
1008 if (!set_nonblocking(
netw->link.sock)) {
1009 n_log(
LOG_ERR,
"n_reactor_register: O_NONBLOCK on socket %d failed: %s",
1010 netw->link.sock, strerror(errno));
1022 reactor_recv_state_reset(
netw);
1023 reactor_send_state_reset(
netw);
1028 __atomic_store_n(&
netw->reactor_close_acked, 0, __ATOMIC_RELEASE);
1030 struct epoll_event ev;
1034 ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
1036 if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_ADD,
netw->link.sock, &ev) != 0) {
1037 n_log(
LOG_ERR,
"n_reactor_register: epoll_ctl ADD socket %d failed: %s",
1038 netw->link.sock, strerror(errno));
1049 __atomic_store_n(&
netw->reactor_registered, 1, __ATOMIC_RELEASE);
1059 pthread_mutex_lock(&reactor->registered_lock);
1061 pthread_mutex_unlock(&reactor->registered_lock);
1063 atomic_fetch_add(&reactor->fds_registered, 1);
1069 if (!reactor || !
netw)
return;
1073 if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_DEL,
netw->link.sock, NULL) != 0) {
1074 if (errno != EBADF && errno != ENOENT) {
1076 "n_reactor_unregister: epoll_ctl DEL socket %d "
1078 netw->link.sock, strerror(errno));
1081 reactor_recv_state_reset(
netw);
1082 reactor_send_state_reset(
netw);
1085 pthread_mutex_lock(&reactor->registered_lock);
1086 LIST_NODE* node = reactor->registered->start;
1088 if (node->
ptr == (
void*)
netw) {
1094 pthread_mutex_unlock(&reactor->registered_lock);
1101 pthread_mutex_lock(&reactor->dirty_lock);
1103 LIST_NODE* dn = reactor->dirty_pending->start;
1112 pthread_mutex_unlock(&reactor->dirty_lock);
1113 __atomic_store_n(&
netw->in_dirty_list, 0, __ATOMIC_RELEASE);
1122 atomic_fetch_add(&reactor->fds_unregistered, 1);
1133 __atomic_store_n(&
netw->reactor_close_acked, 1, __ATOMIC_RELEASE);
1140 if (r->wake_efd < 0)
return;
1151 if (__atomic_compare_exchange_n(&
netw->in_dirty_list, &expected, 1,
1152 0, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
1153 pthread_mutex_lock(&r->dirty_lock);
1155 pthread_mutex_unlock(&r->dirty_lock);
1160 ssize_t w = write(r->wake_efd, &one,
sizeof(one));
1201 while (!__atomic_load_n(&
netw->reactor_close_acked, __ATOMIC_ACQUIRE)) {
1202 struct timespec ts = {0, 1000000L};
1203 nanosleep(&ts, NULL);
1208 size_t send_list_limit,
1209 size_t recv_list_limit,
1214 n_log(
LOG_ERR,
"netw_accept_into_reactor: NULL reactor");
1215 if (retval) *retval = EINVAL;
1219 recv_list_limit, blocking, retval);
1220 if (!
netw)
return NULL;
1225 n_log(
LOG_ERR,
"netw_accept_into_reactor: register failed for socket %d",
1228 if (retval) *retval = EIO;
1241 "n_reactor_new: epoll/eventfd not available on this "
1242 "platform; reactor mode unsupported (use thread mode)");
1247 if (reactor) *reactor = NULL;
1265 if (out) memset(out, 0,
sizeof(*out));
1288 size_t send_list_limit,
1289 size_t recv_list_limit,
1294 (void)send_list_limit;
1295 (void)recv_list_limit;
1298 if (retval) *retval = ENOSYS;
NETWORK * netw
Network for server mode, accepting incomming.
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
void * ptr
void pointer to store
LIST_NODE * start
pointer to the start of the list
struct LIST_NODE * next
pointer to the next node
#define list_shift(__LIST_, __TYPE_)
Shift macro helper for void pointer casting.
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
#define remove_list_node(__LIST_, __NODE_, __TYPE_)
Remove macro helper for void pointer casting.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Structure of a generic LIST container.
Structure of a generic list node.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
#define LOG_WARNING
warning conditions
#define LOG_INFO
informational
N_STR * zip4_nstr(N_STR *src)
Compress src with LZ4 block format.
N_STR * unzip4_nstr(N_STR *src)
Decompress an N_STR produced by zip4_nstr.
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
size_t length
total allocation (in bytes) of the data buffer, padding included
void free_nstr_ptr(void *ptr)
Free a N_STR pointer structure.
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
A box including a string and his lenght.
N_SOCKET link
networking socket
char * reactor_send_buf
malloc'd framed bytes, NULL when idle
SOCKET sock
a normal socket
#define NETW_IO_WANT_READ
single-attempt I/O (send_data_once / recv_data_once): the operation cannot progress until the socket ...
#define NETW_COMPRESS_THRESHOLD
Opportunistic compression policy.
#define NETW_COMPRESS_MIN_RATIO
#define NETW_MSG_MAX_FRAME_SIZE
Maximum accepted size, in bytes, of a single received message frame payload.
ssize_t recv_data_once(void *netw, char *buf, uint32_t n)
single-attempt recv for non-blocking sockets (reactor use).
ssize_t send_data_once(void *netw, char *buf, uint32_t n)
single-attempt send for non-blocking sockets (reactor use).
#define netw_atomic_write_reactor_handle(netw, val)
#define netw_atomic_read_reactor_mode(netw)
Lock-free atomic read of the reactor_mode flag.
NETWORK * netw_accept_from_ex(NETWORK *from, size_t send_list_limit, size_t recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
#define netw_atomic_write_reactor_mode(netw, val)
#define NETW_SOCKET_DISCONNECTED
Code for a disconnected recv.
int netw_set(NETWORK *netw, int flag)
Restart or reset the specified network ability.
int netw_get_state(NETWORK *netw, uint32_t *state, int *thr_engine_status)
Get the state of a network.
#define netw_atomic_read_reactor_handle(netw)
Same contract for the back-pointer to the reactor.
#define NETW_IO_WANT_WRITE
single-attempt I/O: the operation cannot progress until the socket is WRITABLE.
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
@ NETW_COMPRESS_NONE
no automatic compression on send, still decompresses inbound
@ NETW_COMPRESS_LZ4
compress on send with LZ4, decompress either on recv
@ NETW_THR_ENGINE_STARTED
N_STR * unzip_nstr(N_STR *src)
return an uncompressed version of src
N_STR * zip_nstr(N_STR *src)
return a compressed version of src
Common headers and low-level functions & define.
List structures and definitions.
LZ4 block-compression handler.
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_run(n_reactor *reactor)
Run the epoll loop on the calling thread.
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.
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 threa...
void n_reactor_unregister(n_reactor *reactor, NETWORK *netw)
Unregister a NETWORK from the reactor.
int n_reactor_register(n_reactor *reactor, NETWORK *netw)
Register a NETWORK with the reactor.
void n_reactor_notify_send(NETWORK *netw)
Producer-side wake-up after a netw_add_msg.
void n_reactor_close_netw_sync(NETWORK *netw)
Synchronously close a reactor-registered NETWORK from the game thread.
void n_reactor_stop(n_reactor *reactor)
Signal the run loop to exit at the next iteration.
void n_reactor_destroy(n_reactor **reactor)
Tear down a reactor.
Single-threaded epoll reactor for n_network connections.
long long fds_registered
lifetime register call count
long long writes_partial
EAGAIN on send -> re-armed EPOLLOUT.
long long reads_partial
EAGAIN on recv -> kept accumulator.
long long events_processed
total epoll events dispatched
long long wake_signals
eventfd wake events processed
long long wake_walk_visits
long long wake_walk_drains
struct n_reactor n_reactor
Opaque reactor handle.
long long fds_unregistered
lifetime unregister call count
Counters for the dashboard / profile_server.sh.
N_STR and string function declaration.
ZLIB compression handler.