Table of Contents
- ACCEPT_POOL: parallel accept pool for high-performance connection handling
- Overview
- Server Architecture Modes
- Single-inline: accept + handle in one thread
- Single-pool: one accept thread + worker thread pool
- Pooled: N accept threads + worker thread pool
- Performance Considerations
ACCEPT_POOL: parallel accept pool for high-performance connection handling
Structures
| Name | |
|---|---|
| struct | NETW_ACCEPT_POOL_STATS Statistics for the accept pool. |
| struct | NETW_ACCEPT_POOL Structure of a parallel accept pool. |
Types
| Name | |
|---|---|
| typedef void(*)(NETWORK *accepted, void *user_data) | netw_accept_callback_t callback type for accepted connections. |
Functions
| Name | |
|---|---|
| int | netw_accept_pool_wait(NETW_ACCEPT_POOL * pool, int timeout_sec) Wait for all accept threads to finish after a stop request. |
| int | netw_accept_pool_stop(NETW_ACCEPT_POOL * pool) Request the accept pool to stop. |
| int | netw_accept_pool_start(NETW_ACCEPT_POOL * pool) Start the accept pool (launches accept threads). |
| 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_destroy(NETW_ACCEPT_POOL ** pool) Destroy an accept pool. |
| 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. |
Defines
| Name | |
|---|---|
| netw_accept_pool_atomic_write_state(pool, val) Lock-free atomic write of the accept pool state. |
|
| netw_accept_pool_atomic_read_state(pool) Lock-free atomic read of the accept pool state. |
|
| NETW_ACCEPT_POOL_STOPPING accept pool state: stop requested |
|
| NETW_ACCEPT_POOL_STOPPED accept pool state: fully stopped |
|
| NETW_ACCEPT_POOL_RUNNING accept pool state: running and accepting connections |
|
| NETW_ACCEPT_POOL_IDLE accept pool state: idle, not yet started |
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.
Types Documentation
typedef netw_accept_callback_t
typedef void(* netw_accept_callback_t) (NETWORK *accepted, void *user_data);
callback type for accepted connections.
Parameters:
- accepted the newly accepted NETWORK connection (caller takes ownership)
- user_data opaque pointer passed at pool creation
Functions Documentation
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:
- pool the accept pool to wait on
- timeout_sec maximum seconds to wait (0 = wait forever)
Return: TRUE if all threads joined, FALSE on timeout or error
function netw_accept_pool_stop
int netw_accept_pool_stop(
NETW_ACCEPT_POOL * pool
)
Request the accept pool to stop.
Parameters:
- pool the accept pool to stop
Return: TRUE on success, FALSE on error
Non-blocking, threads will finish current accept and exit.
function netw_accept_pool_start
int netw_accept_pool_start(
NETW_ACCEPT_POOL * pool
)
Start the accept pool (launches accept threads).
Parameters:
- pool the accept pool to start
Return: TRUE on success, FALSE on error
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).
Parameters:
- pool the accept pool
- stats output structure to fill
Return: TRUE on success, FALSE on error
Get a snapshot of pool statistics.
Get a snapshot of pool statistics (thread-safe copy).
function netw_accept_pool_destroy
int netw_accept_pool_destroy(
NETW_ACCEPT_POOL ** pool
)
Destroy an accept pool.
Parameters:
- pool pointer to the accept pool pointer (set to NULL)
Return: TRUE on success, FALSE on error
Pool must be stopped first.
Pool must be stopped first.
function 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:
- server listening NETWORK (must already be listening)
- nb_threads number of accept threads
- accept_timeout timeout in msec for accept (0=blocking, -1=nonblocking, >0=timeout ms)
- callback function called for each accepted connection
- user_data opaque pointer passed to callback
Return: new NETW_ACCEPT_POOL or NULL on error
Macros Documentation
define netw_accept_pool_atomic_write_state
#define netw_accept_pool_atomic_write_state(
pool,
val
)
__atomic_store_n(&(pool)->state, (val), __ATOMIC_RELEASE)
Lock-free atomic write of the accept pool state.
define netw_accept_pool_atomic_read_state
#define netw_accept_pool_atomic_read_state(
pool
)
__atomic_load_n(&(pool)->state, __ATOMIC_ACQUIRE)
Lock-free atomic read of the accept pool state.
define NETW_ACCEPT_POOL_STOPPING
#define NETW_ACCEPT_POOL_STOPPING 2
accept pool state: stop requested
define NETW_ACCEPT_POOL_STOPPED
#define NETW_ACCEPT_POOL_STOPPED 3
accept pool state: fully stopped
define NETW_ACCEPT_POOL_RUNNING
#define NETW_ACCEPT_POOL_RUNNING 1
accept pool state: running and accepting connections
define NETW_ACCEPT_POOL_IDLE
#define NETW_ACCEPT_POOL_IDLE 0
accept pool state: idle, not yet started
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB