Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ACCEPT_POOL: parallel accept pool for high-performance connection handling

Data Structures

struct  NETW_ACCEPT_POOL
 Structure of a parallel accept pool. More...
struct  NETW_ACCEPT_POOL_STATS
 Statistics for the accept pool. More...

Macros

#define netw_accept_pool_atomic_read_state(pool)
 Lock-free atomic read of the accept pool state.
#define netw_accept_pool_atomic_write_state(pool, val)
 Lock-free atomic write of the accept pool state.
#define NETW_ACCEPT_POOL_IDLE   0
 accept pool state: idle, not yet started
#define NETW_ACCEPT_POOL_RUNNING   1
 accept pool state: running and accepting connections
#define NETW_ACCEPT_POOL_STOPPED   3
 accept pool state: fully stopped
#define NETW_ACCEPT_POOL_STOPPING   2
 accept pool state: stop requested

Typedefs

typedef void(* netw_accept_callback_t) (NETWORK *accepted, void *user_data)
 callback type for accepted connections.

Functions

NETW_ACCEPT_POOL * netw_accept_pool_create (NETWORK *server, size_t nb_threads, int accept_timeout, netw_accept_callback_t callback, void *user_data)
 Create a new accept pool.
int netw_accept_pool_destroy (NETW_ACCEPT_POOL **pool)
 Destroy an accept pool.
int netw_accept_pool_get_stats (NETW_ACCEPT_POOL *pool, NETW_ACCEPT_POOL_STATS *stats)
 Get a snapshot of pool statistics (thread-safe copy).
int netw_accept_pool_start (NETW_ACCEPT_POOL *pool)
 Start the accept pool (launches accept threads).
int netw_accept_pool_stop (NETW_ACCEPT_POOL *pool)
 Request the accept pool to stop.
int netw_accept_pool_wait (NETW_ACCEPT_POOL *pool, int timeout_sec)
 Wait for all accept threads to finish after a stop request.

Detailed Description

Overview

The accept pool provides nginx-style parallel connection acceptance: multiple threads call accept() on the same listening socket, each invoking a user callback for every accepted connection.

Server Architecture Modes

There are three common ways to structure a server's accept + handling logic. Each has different performance characteristics depending on the workload:

Single-inline: accept + handle in one thread

One thread does accept() and handles the client inline before accepting the next connection. Both accept and handling are fully serialized.

  • Pros: Simplest architecture, no synchronization overhead, no thread contention
  • Cons: Throughput limited to one connection at a time
  • Best for: Low-traffic servers, simple request/response protocols, debugging

Single-pool: one accept thread + worker thread pool

One thread calls accept() and dispatches each connection to a worker thread pool for handling. Accept is serialized but handling is parallelized.

  • Pros: Parallelizes the expensive part (client handling), simple accept logic
  • Cons: Accept is still serialized, under extreme connection rates the single accept thread becomes the bottleneck
  • Best for: Most servers where per-connection work (I/O, computation) dominates the accept overhead. This is the sweet spot for typical workloads.

Pooled: N accept threads + worker thread pool

Multiple threads call accept() on the same listening socket (nginx-style), each dispatching to a worker thread pool. Both accept and handling are parallelized.

  • Pros: Eliminates the accept bottleneck under extreme connection rates
  • Cons: Extra threads and kernel-level accept queue contention add overhead. On localhost or low-latency networks, this overhead can outweigh the benefit, making pooled mode slower than single-pool.
  • Best for: High connection rates over real networks (WAN clients), SSL/TLS servers where the handshake is part of the accept path, multi-socket NUMA systems, or any scenario where accept() itself is slow enough to serialize.

Performance Considerations

On localhost (loopback), accept() is near-instant (nanoseconds). A single accept thread can drain the kernel queue faster than clients can fill it. Adding more accept threads introduces kernel socket lock contention and context switches without benefit, single-pool will often outperform pooled mode in this case.

The pooled accept advantage appears when:

  • Clients connect over a real network with non-trivial latency
  • SSL/TLS handshakes run as part of the accept path
  • Connection rates exceed what one thread can accept (10k+ conn/sec sustained)
  • The system has many CPU cores and the kernel accept lock is the bottleneck

The example programs (ex_accept_pool_server, ex_accept_pool_client, test_accept_pool.sh) demonstrate all three modes with configurable parameters for benchmarking.


Data Structure Documentation

◆ NETW_ACCEPT_POOL

struct NETW_ACCEPT_POOL

Structure of a parallel accept pool.

Examples
ex_accept_pool_server.c.

Definition at line 136 of file n_network_accept_pool.h.

Collaboration diagram for NETW_ACCEPT_POOL:
Data Fields
pthread_t * accept_threads array of pthread ids
int accept_timeout accept timeout in milliseconds (passed to netw_accept_from_ex blocking param).

0 = blocking, -1 = non-blocking, >0 = timeout in msec

netw_accept_callback_t callback user callback invoked on each accepted connection
size_t nb_accept_threads number of accept threads
NETWORK * server listening socket (not owned, caller must keep alive)
uint32_t state atomic pool state
NETW_ACCEPT_POOL_STATS stats pool statistics
pthread_mutex_t stats_lock mutex protecting stats
void * user_data opaque user data passed to callback

◆ NETW_ACCEPT_POOL_STATS

struct NETW_ACCEPT_POOL_STATS

Statistics for the accept pool.

Examples
ex_accept_pool_server.c.

Definition at line 122 of file n_network_accept_pool.h.

Collaboration diagram for NETW_ACCEPT_POOL_STATS:
Data Fields
size_t active_threads number of accept threads currently running
struct timespec start_time time when pool was started (CLOCK_MONOTONIC)
size_t total_accepted total connections successfully accepted
size_t total_errors total accept errors
size_t total_timeouts total accept timeouts (no connection available)

Macro Definition Documentation

◆ netw_accept_pool_atomic_read_state

#define netw_accept_pool_atomic_read_state ( pool)
Value:
__atomic_load_n(&(pool)->state, __ATOMIC_ACQUIRE)
static NETWORK_POOL * pool

Lock-free atomic read of the accept pool state.

Definition at line 159 of file n_network_accept_pool.h.

Referenced by netw_accept_pool_destroy(), netw_accept_pool_start(), netw_accept_pool_stop(), netw_accept_pool_thread_func(), and netw_accept_pool_wait().

◆ netw_accept_pool_atomic_write_state

#define netw_accept_pool_atomic_write_state ( pool,
val )
Value:
__atomic_store_n(&(pool)->state, (val), __ATOMIC_RELEASE)

Lock-free atomic write of the accept pool state.

Definition at line 162 of file n_network_accept_pool.h.

Referenced by netw_accept_pool_create(), netw_accept_pool_start(), netw_accept_pool_stop(), and netw_accept_pool_wait().

◆ NETW_ACCEPT_POOL_IDLE

#define NETW_ACCEPT_POOL_IDLE   0

accept pool state: idle, not yet started

Definition at line 107 of file n_network_accept_pool.h.

Referenced by netw_accept_pool_create(), netw_accept_pool_start(), and netw_accept_pool_wait().

◆ NETW_ACCEPT_POOL_RUNNING

#define NETW_ACCEPT_POOL_RUNNING   1

accept pool state: running and accepting connections

Definition at line 109 of file n_network_accept_pool.h.

Referenced by netw_accept_pool_destroy(), netw_accept_pool_start(), netw_accept_pool_stop(), netw_accept_pool_thread_func(), and netw_accept_pool_wait().

◆ NETW_ACCEPT_POOL_STOPPED

#define NETW_ACCEPT_POOL_STOPPED   3

accept pool state: fully stopped

Definition at line 113 of file n_network_accept_pool.h.

Referenced by netw_accept_pool_start(), and netw_accept_pool_wait().

◆ NETW_ACCEPT_POOL_STOPPING

#define NETW_ACCEPT_POOL_STOPPING   2

accept pool state: stop requested

Definition at line 111 of file n_network_accept_pool.h.

Referenced by netw_accept_pool_destroy(), netw_accept_pool_start(), and netw_accept_pool_stop().

Typedef Documentation

◆ netw_accept_callback_t

typedef void(* netw_accept_callback_t) (NETWORK *accepted, void *user_data)

callback type for accepted connections.

Parameters
acceptedthe newly accepted NETWORK connection (caller takes ownership)
user_dataopaque pointer passed at pool creation

Definition at line 119 of file n_network_accept_pool.h.

Function Documentation

◆ netw_accept_pool_create()

NETW_ACCEPT_POOL * netw_accept_pool_create ( NETWORK * server,
size_t nb_threads,
int accept_timeout,
netw_accept_callback_t callback,
void * user_data )

Create a new accept pool.

Parameters
serverlistening NETWORK (must already be listening)
nb_threadsnumber of accept threads
accept_timeouttimeout in msec for accept (0=blocking, -1=nonblocking, >0=timeout ms)
callbackfunction called for each accepted connection
user_dataopaque pointer passed to callback
Returns
new NETW_ACCEPT_POOL or NULL on error

Definition at line 111 of file n_network_accept_pool.c.

References __n_assert, accept_timeout, Free, LOG_ERR, Malloc, n_log, netw_accept_pool_atomic_write_state, NETW_ACCEPT_POOL_IDLE, pool, and server.

Referenced by main().

Here is the caller graph for this function:

◆ netw_accept_pool_destroy()

int netw_accept_pool_destroy ( NETW_ACCEPT_POOL ** pool)

Destroy an accept pool.

Pool must be stopped first.

Pool must be stopped first.

Parameters
poolpointer to the accept pool pointer (set to NULL)
Returns
TRUE on success, FALSE on error

Definition at line 268 of file n_network_accept_pool.c.

References __n_assert, Free, LOG_WARNING, n_log, netw_accept_pool_atomic_read_state, NETW_ACCEPT_POOL_RUNNING, netw_accept_pool_stop(), NETW_ACCEPT_POOL_STOPPING, netw_accept_pool_wait(), and pool.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ netw_accept_pool_get_stats()

int netw_accept_pool_get_stats ( NETW_ACCEPT_POOL * pool,
NETW_ACCEPT_POOL_STATS * stats )

Get a snapshot of pool statistics (thread-safe copy).

Get a snapshot of pool statistics (thread-safe copy).

Parameters
poolthe accept pool
statsoutput structure to fill
Returns
TRUE on success, FALSE on error

Definition at line 252 of file n_network_accept_pool.c.

References __n_assert, and pool.

Referenced by main().

Here is the caller graph for this function:

◆ netw_accept_pool_start()

int netw_accept_pool_start ( NETW_ACCEPT_POOL * pool)

Start the accept pool (launches accept threads).

Parameters
poolthe accept pool to start
Returns
TRUE on success, FALSE on error

Definition at line 156 of file n_network_accept_pool.c.

References __n_assert, LOG_ERR, LOG_NOTICE, n_log, netw_accept_pool_atomic_read_state, netw_accept_pool_atomic_write_state, NETW_ACCEPT_POOL_IDLE, NETW_ACCEPT_POOL_RUNNING, NETW_ACCEPT_POOL_STOPPED, NETW_ACCEPT_POOL_STOPPING, netw_accept_pool_thread_func(), and pool.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ netw_accept_pool_stop()

int netw_accept_pool_stop ( NETW_ACCEPT_POOL * pool)

Request the accept pool to stop.

Non-blocking, threads will finish current accept and exit.

Parameters
poolthe accept pool to stop
Returns
TRUE on success, FALSE on error

Definition at line 196 of file n_network_accept_pool.c.

References __n_assert, LOG_NOTICE, LOG_WARNING, n_log, netw_accept_pool_atomic_read_state, netw_accept_pool_atomic_write_state, NETW_ACCEPT_POOL_RUNNING, NETW_ACCEPT_POOL_STOPPING, and pool.

Referenced by main(), netw_accept_pool_destroy(), and netw_accept_pool_wait().

Here is the caller graph for this function:

◆ netw_accept_pool_wait()

int netw_accept_pool_wait ( NETW_ACCEPT_POOL * pool,
int timeout_sec )

Wait for all accept threads to finish after a stop request.

Parameters
poolthe accept pool to wait on
timeout_secmaximum seconds to wait (0 = wait forever)
Returns
TRUE if all threads joined, FALSE on timeout or error

Definition at line 222 of file n_network_accept_pool.c.

References __n_assert, LOG_NOTICE, LOG_WARNING, n_log, netw_accept_pool_atomic_read_state, netw_accept_pool_atomic_write_state, NETW_ACCEPT_POOL_IDLE, NETW_ACCEPT_POOL_RUNNING, netw_accept_pool_stop(), NETW_ACCEPT_POOL_STOPPED, and pool.

Referenced by main(), and netw_accept_pool_destroy().

Here is the call graph for this function:
Here is the caller graph for this function: