81#define MODE_SINGLE_INLINE 0
82#define MODE_SINGLE_POOL 1
162 arg->client = client;
163 arg->cb_data = cb_data;
178 pthread_mutex_lock(&cb_data->
lock);
181 pthread_mutex_unlock(&cb_data->
lock);
206 "Usage: ex_accept_pool_server [options]\n"
207 " -p PORT port to listen on (required)\n"
208 " -a ADDR address to bind to (optional, default: all)\n"
209 " -m MODE 'single-inline', 'single-pool', or 'pooled' (default: single-inline)\n"
210 " -n COUNT number of connections to handle (default: 500)\n"
211 " -t THREADS number of threads for pool/accept (default: 4)\n"
212 " -w SECONDS give up after SECONDS without progress, 0 to wait forever (default: 30)\n"
213 " -V LEVEL log level: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_ERR (default: LOG_NOTICE)\n"
214 " -h show this help\n");
217int main(
int argc,
char** argv) {
220 char* mode_str =
"single-inline";
225 while ((opt = getopt(argc, argv,
"hp:a:m:n:t:w:V:")) != -1) {
228 port = strdup(optarg);
231 addr = strdup(optarg);
240 nb_threads = atoi(optarg);
246 if (!strcmp(optarg,
"LOG_DEBUG"))
248 else if (!strcmp(optarg,
"LOG_INFO"))
250 else if (!strcmp(optarg,
"LOG_NOTICE"))
252 else if (!strcmp(optarg,
"LOG_ERR"))
263 fprintf(stderr,
"Error: -p PORT is required\n");
269 if (strcmp(mode_str,
"single-pool") == 0) {
271 }
else if (strcmp(mode_str,
"pooled") == 0) {
273 }
else if (strcmp(mode_str,
"single-inline") != 0) {
274 fprintf(stderr,
"Error: unknown mode '%s'\n", mode_str);
284 signal(SIGPIPE, SIG_IGN);
298 pthread_mutex_init(&cb_data.
lock, NULL);
300 struct timespec t_start, t_end;
301 clock_gettime(CLOCK_MONOTONIC, &t_start);
303 const char* mode_names[] = {
"SINGLE-INLINE",
"SINGLE-POOL",
"POOLED"};
311 time_t idle_msec = 0;
312 size_t seen_progress = 0;
318 n_log(
LOG_NOTICE,
"1 thread: accept + handle inline (fully serialized)");
321 pthread_mutex_lock(&cb_data.
lock);
323 pthread_mutex_unlock(&cb_data.
lock);
328 if (h != seen_progress) {
356 int accepted_count = 0;
359 if ((
size_t)accepted_count != seen_progress) {
360 seen_progress = (size_t)accepted_count;
385 n_log(
LOG_NOTICE,
"%d accept threads, %d worker threads", nb_threads, nb_threads);
410 pthread_mutex_lock(&cb_data.
lock);
412 pthread_mutex_unlock(&cb_data.
lock);
418 if (h != seen_progress) {
430 n_log(
LOG_INFO,
"stats: accepted=%zu errors=%zu timeouts=%zu active_threads=%zu",
452 clock_gettime(CLOCK_MONOTONIC, &t_end);
453 double elapsed = (double)(t_end.tv_sec - t_start.tv_sec) +
454 (double)(t_end.tv_nsec - t_start.tv_nsec) / 1e9;
456 n_log(
LOG_NOTICE,
"=== DONE: %zu connections in %.3f seconds (%.1f conn/sec) ===",
460 pthread_mutex_destroy(&cb_data.
lock);
467 if (exit_code == 0) {
470 n_log(
LOG_ERR,
"Server exited with code %d", exit_code);
static void * worker_handle_client(void *ptr)
Worker thread function: handles a client and updates counter.
size_t handled
atomic counter of handled connections
static void sighandler(int sig)
pthread_mutex_t lock
mutex for counter
static volatile int server_running
NETWORK * client
the accepted connection
static void handle_inline_and_count(NETWORK *client, CALLBACK_DATA *cb_data)
Handle client inline in the accept thread and update counter.
THREAD_POOL * worker_pool
thread pool for dispatching (modes single-pool and pooled)
static void on_accept_pooled(NETWORK *conn, void *user_data)
Accept pool callback: dispatches to thread pool.
#define MODE_SINGLE_INLINE
mode constants
CALLBACK_DATA * cb_data
back pointer to callback data for counting
static void handle_client(NETWORK *client)
Handle a single client: read one message, echo it back, close.
static void dispatch_to_pool(NETWORK *client, CALLBACK_DATA *cb_data)
Dispatch a client to the thread pool for handling.
data passed to accept callback and worker threads
data passed to worker thread function
size_t total_errors
total accept errors
size_t active_threads
number of accept threads currently running
size_t total_timeouts
total accept timeouts (no connection available)
size_t total_accepted
total connections successfully accepted
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_get_stats(NETW_ACCEPT_POOL *pool, NETW_ACCEPT_POOL_STATS *stats)
Get a snapshot of pool statistics.
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_destroy(NETW_ACCEPT_POOL **pool)
Destroy an accept pool.
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).
Structure of a parallel accept pool.
Statistics for the accept pool.
#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.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NOTICE
normal but significant condition
#define LOG_INFO
informational
A box including a string and his lenght.
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
int start_HiTimer(N_TIME *timer)
Initialize or restart from zero any N_TIME HiTimer.
time_t get_msec(N_TIME *timer)
Poll any N_TIME HiTimer, returning msec, and moving currentTime to startTime.
int netw_add_msg(NETWORK *netw, N_STR *msg)
Add a message to send in aimed NETWORK.
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
int netw_start_thr_engine(NETWORK *netw)
Start the NETWORK netw Threaded Engine.
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 NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
N_STR * netw_wait_msg(NETWORK *netw, unsigned int refresh, size_t timeout)
Wait a message from aimed NETWORK.
#define NORMAL_PROC
processing mode for added func, synced start, can be queued
THREAD_POOL * new_thread_pool(size_t nbmaxthr, size_t nb_max_waiting)
Create a new pool of nbmaxthr threads.
int add_threaded_process(THREAD_POOL *thread_pool, void *(*func_ptr)(void *param), void *param, int mode)
add a function and params to a thread pool
int wait_for_threaded_pool(THREAD_POOL *thread_pool)
Wait for the thread pool to become idle (no active threads, empty waiting list), blocking without pol...
int destroy_threaded_pool(THREAD_POOL **pool, unsigned int delay)
Stop every thread of a pool and free it.
Structure of a thread pool.
Common headers and low-level functions & define.
Accept pool for parallel connection acceptance (nginx-style).