Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_network_accept_pool.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
29
30#include <errno.h>
31#include <string.h>
32
40static void* netw_accept_pool_thread_func(void* arg) {
42
43 __n_assert(pool, return NULL);
44 __n_assert(pool->server, return NULL);
45 __n_assert(pool->callback, return NULL);
46
47 pthread_mutex_lock(&pool->stats_lock);
48 pool->stats.active_threads++;
49 pthread_mutex_unlock(&pool->stats_lock);
50
52 int retval = 0;
53 NETWORK* accepted = netw_accept_from_ex(pool->server, 0, 0, pool->accept_timeout, &retval);
54
55 /* check if we should exit */
57 if (accepted) {
58 netw_close(&accepted);
59 }
60 break;
61 }
62
63 if (accepted) {
64 pthread_mutex_lock(&pool->stats_lock);
65 pool->stats.total_accepted++;
66 pthread_mutex_unlock(&pool->stats_lock);
67
68 pool->callback(accepted, pool->user_data);
69 } else {
70 if (retval == EINTR) {
71 /* interrupted, just retry */
72 continue;
73 }
74 if (retval == EAGAIN || retval == EWOULDBLOCK || retval == 0) {
75 /* timeout or no connection available */
76 pthread_mutex_lock(&pool->stats_lock);
77 pool->stats.total_timeouts++;
78 pthread_mutex_unlock(&pool->stats_lock);
79 /* small sleep to avoid busy-looping on non-blocking accept */
80 if (pool->accept_timeout == -1) {
81 u_sleep(1000);
82 }
83 } else {
84 /* actual error */
85 pthread_mutex_lock(&pool->stats_lock);
86 pool->stats.total_errors++;
87 pthread_mutex_unlock(&pool->stats_lock);
88 n_log(LOG_ERR, "accept pool thread: accept error, retval=%d", retval);
89 }
90 }
91 }
92
93 pthread_mutex_lock(&pool->stats_lock);
94 if (pool->stats.active_threads > 0) {
95 pool->stats.active_threads--;
96 }
97 pthread_mutex_unlock(&pool->stats_lock);
98
99 return NULL;
100}
101
111NETW_ACCEPT_POOL* netw_accept_pool_create(NETWORK* server, size_t nb_threads, int accept_timeout, netw_accept_callback_t callback, void* user_data) {
112 __n_assert(server, return NULL);
113 __n_assert(callback, return NULL);
114
115 if (nb_threads == 0) {
116 n_log(LOG_ERR, "netw_accept_pool_create: nb_threads must be > 0");
117 return NULL;
118 }
119
120 NETW_ACCEPT_POOL* pool = NULL;
122 __n_assert(pool, return NULL);
123
124 pool->server = server;
125 pool->nb_accept_threads = nb_threads;
126 pool->accept_timeout = accept_timeout;
127 pool->callback = callback;
128 pool->user_data = user_data;
129
131
132 memset(&pool->stats, 0, sizeof(NETW_ACCEPT_POOL_STATS));
133
134 if (pthread_mutex_init(&pool->stats_lock, NULL) != 0) {
135 n_log(LOG_ERR, "netw_accept_pool_create: failed to init stats mutex");
136 Free(pool);
137 return NULL;
138 }
139
140 Malloc(pool->accept_threads, pthread_t, nb_threads);
141 if (!pool->accept_threads) {
142 n_log(LOG_ERR, "netw_accept_pool_create: failed to allocate thread array");
143 pthread_mutex_destroy(&pool->stats_lock);
144 Free(pool);
145 return NULL;
146 }
147
148 return pool;
149}
150
157 __n_assert(pool, return FALSE);
158
159 uint32_t current = netw_accept_pool_atomic_read_state(pool);
160 if (current != NETW_ACCEPT_POOL_IDLE && current != NETW_ACCEPT_POOL_STOPPED) {
161 n_log(LOG_ERR, "netw_accept_pool_start: pool is not idle or stopped (state=%u)", current);
162 return FALSE;
163 }
164
165 /* reset stats */
166 pthread_mutex_lock(&pool->stats_lock);
167 memset(&pool->stats, 0, sizeof(NETW_ACCEPT_POOL_STATS));
168 clock_gettime(CLOCK_MONOTONIC, &pool->stats.start_time);
169 pthread_mutex_unlock(&pool->stats_lock);
170
172
173 for (size_t i = 0; i < pool->nb_accept_threads; i++) {
174 int err = pthread_create(&pool->accept_threads[i], NULL, netw_accept_pool_thread_func, pool);
175 if (err != 0) {
176 n_log(LOG_ERR, "netw_accept_pool_start: failed to create thread %zu: %s", i, strerror(err));
177 /* stop already-created threads */
179 for (size_t j = 0; j < i; j++) {
180 pthread_join(pool->accept_threads[j], NULL);
181 }
183 return FALSE;
184 }
185 }
186
187 n_log(LOG_NOTICE, "accept pool started with %zu threads", pool->nb_accept_threads);
188 return TRUE;
189}
190
197 __n_assert(pool, return FALSE);
198
199 uint32_t current = netw_accept_pool_atomic_read_state(pool);
200 if (current != NETW_ACCEPT_POOL_RUNNING) {
201 n_log(LOG_WARNING, "netw_accept_pool_stop: pool is not running (state=%u)", current);
202 return FALSE;
203 }
204
205 n_log(LOG_NOTICE, "accept pool: stop requested");
207
208 /* shutdown the listening socket to unblock any threads stuck in select()/accept() */
209 if (pool->server && pool->server->link.sock != INVALID_SOCKET) {
210 shutdown(pool->server->link.sock, SHUT_RDWR);
211 }
212
213 return TRUE;
214}
215
223 __n_assert(pool, return FALSE);
224
225 uint32_t current = netw_accept_pool_atomic_read_state(pool);
226 if (current == NETW_ACCEPT_POOL_IDLE || current == NETW_ACCEPT_POOL_STOPPED) {
227 return TRUE;
228 }
229
230 if (current == NETW_ACCEPT_POOL_RUNNING) {
231 n_log(LOG_WARNING, "netw_accept_pool_wait: pool still running, calling stop first");
233 }
234
235 (void)timeout_sec;
236 for (size_t i = 0; i < pool->nb_accept_threads; i++) {
237 pthread_join(pool->accept_threads[i], NULL);
238 }
239
241 n_log(LOG_NOTICE, "accept pool: all threads joined");
242
243 return TRUE;
244}
245
253 __n_assert(pool, return FALSE);
254 __n_assert(stats, return FALSE);
255
256 pthread_mutex_lock(&pool->stats_lock);
257 memcpy(stats, &pool->stats, sizeof(NETW_ACCEPT_POOL_STATS));
258 pthread_mutex_unlock(&pool->stats_lock);
259
260 return TRUE;
261}
262
269 __n_assert(pool, return FALSE);
270 __n_assert((*pool), return FALSE);
271
272 uint32_t current = netw_accept_pool_atomic_read_state(*pool);
273 if (current == NETW_ACCEPT_POOL_RUNNING || current == NETW_ACCEPT_POOL_STOPPING) {
274 n_log(LOG_WARNING, "netw_accept_pool_destroy: pool still active, stopping and waiting");
277 }
278
279 pthread_mutex_destroy(&(*pool)->stats_lock);
280 Free((*pool)->accept_threads);
281 Free(*pool);
282
283 return TRUE;
284}
static NETWORK_POOL * pool
NETWORK * server
#define NETW_ACCEPT_POOL_RUNNING
accept pool state: running and accepting connections
void(* netw_accept_callback_t)(NETWORK *accepted, void *user_data)
callback type for accepted connections.
#define NETW_ACCEPT_POOL_IDLE
accept pool state: idle, not yet started
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.
#define netw_accept_pool_atomic_read_state(pool)
Lock-free atomic read of the accept pool state.
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)
#define netw_accept_pool_atomic_write_state(pool, val)
Lock-free atomic write of the accept pool state.
#define NETW_ACCEPT_POOL_STOPPING
accept pool state: stop requested
#define NETW_ACCEPT_POOL_STOPPED
accept pool state: fully stopped
Structure of a parallel accept pool.
Statistics for the accept pool.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_ERR
error conditions
Definition n_log.h:76
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
#define LOG_WARNING
warning conditions
Definition n_log.h:78
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
Definition n_time.c:54
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' .
Definition n_network.c:3358
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:2662
Structure of a NETWORK.
Definition n_network.h:309
static void * netw_accept_pool_thread_func(void *arg)
Thread function for accept pool workers.
Accept pool for parallel connection acceptance (nginx-style)