@velox0/cerver 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/runtime/server.c CHANGED
@@ -20,6 +20,7 @@
20
20
  #include <fcntl.h>
21
21
  #include <time.h>
22
22
  #include <sys/mman.h>
23
+ #include <sys/time.h>
23
24
  #include <sys/socket.h>
24
25
  #include <sys/types.h>
25
26
  #include <netinet/in.h>
@@ -28,63 +29,62 @@
28
29
  #include <pthread.h>
29
30
 
30
31
  #if defined(__APPLE__) || defined(__FreeBSD__)
31
- #define CERVER_USE_KQUEUE 1
32
- #include <sys/event.h>
32
+ #define CERVER_USE_KQUEUE 1
33
+ #include <sys/event.h>
33
34
  #elif defined(__linux__)
34
- #define CERVER_USE_EPOLL 1
35
- #include <sys/epoll.h>
36
- #include <sys/sendfile.h>
35
+ #define CERVER_USE_EPOLL 1
36
+ #include <sys/epoll.h>
37
+ #include <sys/sendfile.h>
37
38
  #else
38
- #define CERVER_USE_SELECT 1
39
- #include <sys/select.h>
39
+ #define CERVER_USE_SELECT 1
40
+ #include <sys/select.h>
40
41
  #endif
41
42
 
42
43
  #ifdef __linux__
43
- #include <sched.h>
44
+ #include <sched.h>
44
45
  #endif
45
46
 
46
47
  /* Connection pool sizing */
47
- #define CERVER_CONN_POOL_SIZE 128
48
- #define CERVER_CONN_QUEUE_SIZE 4096
48
+ #define CERVER_CONN_POOL_SIZE 128
49
+ #define CERVER_CONN_QUEUE_SIZE 4096
49
50
 
50
51
  /* ------------------------------------------------------------------ */
51
52
  /* memmem fallback */
52
53
  /* ------------------------------------------------------------------ */
53
54
 
54
55
  #if !defined(__APPLE__) && !defined(__linux__) && !defined(_GNU_SOURCE)
55
- static void *cerver_memmem(const void *hay, size_t haylen,
56
- const void *needle, size_t nlen) {
57
- if (nlen == 0) return (void *)hay;
58
- if (nlen > haylen) return NULL;
59
- const char *p = (const char *)hay;
60
- const char *end = p + haylen - nlen;
61
- for (; p <= end; p++) {
62
- if (memcmp(p, needle, nlen) == 0) return (void *)p;
63
- }
64
- return NULL;
56
+ static void* cerver_memmem(const void* hay, size_t haylen, const void* needle, size_t nlen) {
57
+ if (nlen == 0) return (void*)hay;
58
+ if (nlen > haylen) return NULL;
59
+ const char* p = (const char*)hay;
60
+ const char* end = p + haylen - nlen;
61
+ for (; p <= end; p++) {
62
+ if (memcmp(p, needle, nlen) == 0) return (void*)p;
63
+ }
64
+ return NULL;
65
65
  }
66
66
  #define memmem cerver_memmem
67
67
  #endif
68
68
 
69
69
  /* Global for signal handler */
70
- static cerver_server_t *g_srv = NULL;
70
+ static cerver_server_t* g_srv = NULL;
71
71
 
72
72
  static void signal_handler(int sig) {
73
- (void)sig;
74
- if (g_srv) g_srv->running = 0;
73
+ (void)sig;
74
+ if (g_srv) g_srv->running = 0;
75
75
  }
76
76
 
77
77
  static int set_nonblocking(int fd) {
78
- int flags = fcntl(fd, F_GETFL, 0);
79
- if (flags < 0) return -1;
80
- return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
78
+ int flags = fcntl(fd, F_GETFL, 0);
79
+ if (flags < 0) return -1;
80
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
81
81
  }
82
82
 
83
83
  static int get_cpu_count(void) {
84
- long n = sysconf(_SC_NPROCESSORS_ONLN);
85
- if (n < 1) n = 1;
86
- if (n > 64) n = 64;
87
- return (int)n;
84
+ long n = sysconf(_SC_NPROCESSORS_ONLN);
85
+ if (n < 1) n = 1;
86
+ if (n > 64) n = 64;
87
+ return (int)n;
88
88
  }
89
89
 
90
90
  /* ------------------------------------------------------------------ */
@@ -92,178 +92,246 @@ static int get_cpu_count(void) {
92
92
  /* ------------------------------------------------------------------ */
93
93
 
94
94
  typedef struct {
95
- int fds[CERVER_CONN_QUEUE_SIZE];
96
- int head;
97
- int tail;
98
- int count;
99
- pthread_mutex_t lock;
100
- pthread_cond_t not_empty;
95
+ int fds[CERVER_CONN_QUEUE_SIZE];
96
+ int head;
97
+ int tail;
98
+ int count;
99
+ pthread_mutex_t lock;
100
+ pthread_cond_t not_empty;
101
101
  } conn_queue_t;
102
102
 
103
- static void cq_init(conn_queue_t *q) {
104
- memset(q->fds, -1, sizeof(q->fds));
105
- q->head = q->tail = q->count = 0;
106
- pthread_mutex_init(&q->lock, NULL);
107
- pthread_cond_init(&q->not_empty, NULL);
103
+ static void cq_init(conn_queue_t* q) {
104
+ memset(q->fds, -1, sizeof(q->fds));
105
+ q->head = q->tail = q->count = 0;
106
+ pthread_mutex_init(&q->lock, NULL);
107
+ pthread_cond_init(&q->not_empty, NULL);
108
108
  }
109
109
 
110
- static void cq_destroy(conn_queue_t *q) {
111
- pthread_mutex_destroy(&q->lock);
112
- pthread_cond_destroy(&q->not_empty);
110
+ static void cq_destroy(conn_queue_t* q) {
111
+ pthread_mutex_destroy(&q->lock);
112
+ pthread_cond_destroy(&q->not_empty);
113
113
  }
114
114
 
115
115
  /* Returns 0 on success, -1 if queue is full (drop connection). */
116
- static int cq_push(conn_queue_t *q, int fd) {
117
- pthread_mutex_lock(&q->lock);
118
- if (q->count >= CERVER_CONN_QUEUE_SIZE) {
119
- pthread_mutex_unlock(&q->lock);
120
- return -1;
121
- }
122
- q->fds[q->tail] = fd;
123
- q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
124
- q->count++;
125
- pthread_cond_signal(&q->not_empty);
116
+ static int cq_push(conn_queue_t* q, int fd) {
117
+ pthread_mutex_lock(&q->lock);
118
+ if (q->count >= CERVER_CONN_QUEUE_SIZE) {
126
119
  pthread_mutex_unlock(&q->lock);
127
- return 0;
120
+ return -1;
121
+ }
122
+ q->fds[q->tail] = fd;
123
+ q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
124
+ q->count++;
125
+ pthread_cond_signal(&q->not_empty);
126
+ pthread_mutex_unlock(&q->lock);
127
+ return 0;
128
128
  }
129
129
 
130
130
  /* Returns fd, or -1 on shutdown. */
131
- static int cq_pop(conn_queue_t *q, volatile int *running) {
132
- pthread_mutex_lock(&q->lock);
133
- while (q->count == 0 && *running) {
134
- /* Timed wait so we can check running flag periodically */
135
- struct timespec ts;
136
- clock_gettime(CLOCK_REALTIME, &ts);
137
- ts.tv_sec += 1;
138
- pthread_cond_timedwait(&q->not_empty, &q->lock, &ts);
139
- }
140
- if (q->count == 0) {
141
- pthread_mutex_unlock(&q->lock);
142
- return -1;
143
- }
144
- int fd = q->fds[q->head];
145
- q->head = (q->head + 1) % CERVER_CONN_QUEUE_SIZE;
146
- q->count--;
131
+ static int cq_pop(conn_queue_t* q, volatile int* running) {
132
+ pthread_mutex_lock(&q->lock);
133
+ while (q->count == 0 && *running) {
134
+ /* Timed wait so we can check running flag periodically */
135
+ struct timespec ts;
136
+ clock_gettime(CLOCK_REALTIME, &ts);
137
+ ts.tv_sec += 1;
138
+ pthread_cond_timedwait(&q->not_empty, &q->lock, &ts);
139
+ }
140
+ if (q->count == 0) {
147
141
  pthread_mutex_unlock(&q->lock);
148
- return fd;
142
+ return -1;
143
+ }
144
+ int fd = q->fds[q->head];
145
+ q->head = (q->head + 1) % CERVER_CONN_QUEUE_SIZE;
146
+ q->count--;
147
+ pthread_mutex_unlock(&q->lock);
148
+ return fd;
149
149
  }
150
150
 
151
151
  /* Global connection queue */
152
152
  static conn_queue_t g_conn_queue;
153
153
 
154
+ /* Global worker readiness tracking */
155
+ typedef struct {
156
+ int workers_ready;
157
+ int workers_expected;
158
+ pthread_mutex_t lock;
159
+ pthread_cond_t ready_cv;
160
+ } worker_readiness_t;
161
+
162
+ static worker_readiness_t g_worker_readiness = {.workers_ready = 0,
163
+ .workers_expected = 0,
164
+ .lock = PTHREAD_MUTEX_INITIALIZER,
165
+ .ready_cv = PTHREAD_COND_INITIALIZER};
166
+
167
+ /* Global acceptor startup tracking */
168
+ typedef struct {
169
+ int acceptors_ready;
170
+ int start_accepting;
171
+ pthread_mutex_t lock;
172
+ pthread_cond_t ready_cv;
173
+ pthread_cond_t start_cv;
174
+ } acceptor_readiness_t;
175
+
176
+ static acceptor_readiness_t g_acceptor_readiness = {.acceptors_ready = 0,
177
+ .start_accepting = 0,
178
+ .lock = PTHREAD_MUTEX_INITIALIZER,
179
+ .ready_cv = PTHREAD_COND_INITIALIZER,
180
+ .start_cv = PTHREAD_COND_INITIALIZER};
181
+
154
182
  /* ------------------------------------------------------------------ */
155
183
  /* Buffered read */
156
184
  /* ------------------------------------------------------------------ */
157
185
 
158
- static char *read_full_request(int fd, size_t *out_len) {
159
- size_t cap = CERVER_READ_BUF;
160
- size_t len = 0;
161
- char *buf = malloc(cap + 1);
162
- if (!buf) return NULL;
163
-
164
- while (len < (size_t)CERVER_READ_BUF_MAX) {
165
- ssize_t n = read(fd, buf + len, cap - len);
166
- if (n <= 0) break;
167
- len += (size_t)n;
168
- if (len >= 4 && memmem(buf, len, "\r\n\r\n", 4)) break;
169
- if (len == cap) {
170
- size_t newcap = cap * 2;
171
- if (newcap > (size_t)CERVER_READ_BUF_MAX)
172
- newcap = (size_t)CERVER_READ_BUF_MAX;
173
- char *tmp = realloc(buf, newcap + 1);
174
- if (!tmp) { free(buf); return NULL; }
175
- buf = tmp;
176
- cap = newcap;
177
- }
186
+ static char* read_full_request(int fd, size_t* out_len) {
187
+ size_t cap = CERVER_READ_BUF;
188
+ size_t len = 0;
189
+ char* buf = malloc(cap + 1);
190
+ if (!buf) return NULL;
191
+
192
+ while (len < (size_t)CERVER_READ_BUF_MAX) {
193
+ ssize_t n = read(fd, buf + len, cap - len);
194
+ if (n <= 0) break;
195
+ len += (size_t)n;
196
+ if (len >= 4 && memmem(buf, len, "\r\n\r\n", 4)) break;
197
+ if (len == cap) {
198
+ size_t newcap = cap * 2;
199
+ if (newcap > (size_t)CERVER_READ_BUF_MAX) newcap = (size_t)CERVER_READ_BUF_MAX;
200
+ char* tmp = realloc(buf, newcap + 1);
201
+ if (!tmp) {
202
+ free(buf);
203
+ return NULL;
204
+ }
205
+ buf = tmp;
206
+ cap = newcap;
178
207
  }
208
+ }
179
209
 
180
- if (len == 0) { free(buf); return NULL; }
181
- buf[len] = '\0';
182
- *out_len = len;
183
- return buf;
210
+ if (len == 0) {
211
+ free(buf);
212
+ return NULL;
213
+ }
214
+ buf[len] = '\0';
215
+ *out_len = len;
216
+ return buf;
184
217
  }
185
218
 
186
219
  /* ------------------------------------------------------------------ */
187
220
  /* Handle connection with keep-alive */
188
221
  /* ------------------------------------------------------------------ */
189
222
 
190
- static void handle_connection(cerver_server_t *srv, int client_fd) {
191
- int flags = fcntl(client_fd, F_GETFL, 0);
192
- if (flags >= 0 && (flags & O_NONBLOCK))
193
- fcntl(client_fd, F_SETFL, flags & ~O_NONBLOCK);
223
+ static void handle_connection(cerver_server_t* srv, int client_fd) {
224
+ int flags = fcntl(client_fd, F_GETFL, 0);
225
+ if (flags >= 0 && (flags & O_NONBLOCK)) fcntl(client_fd, F_SETFL, flags & ~O_NONBLOCK);
194
226
 
195
- int nodelay = 1;
196
- setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
227
+ int nodelay = 1;
228
+ setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
197
229
 
198
- int request_count = 0;
199
- int keepalive = 1;
230
+ int request_count = 0;
231
+ int keepalive = 1;
200
232
 
201
- while (keepalive && srv->running && request_count < CERVER_KEEPALIVE_MAX) {
202
- struct timeval tv;
203
- tv.tv_sec = (request_count == 0) ? 5 : CERVER_KEEPALIVE_TIMEOUT;
204
- tv.tv_usec = 0;
205
- setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
233
+ while (keepalive && srv->running && request_count < CERVER_KEEPALIVE_MAX) {
234
+ struct timeval tv;
235
+ tv.tv_sec = (request_count == 0) ? 5 : CERVER_KEEPALIVE_TIMEOUT;
236
+ tv.tv_usec = 0;
237
+ setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
206
238
 
207
- size_t req_len = 0;
208
- char *buf = read_full_request(client_fd, &req_len);
209
- if (!buf || req_len == 0) {
210
- if (buf) free(buf);
211
- break;
212
- }
239
+ size_t req_len = 0;
240
+ char* buf = read_full_request(client_fd, &req_len);
241
+ if (!buf || req_len == 0) {
242
+ if (buf) free(buf);
243
+ break;
244
+ }
213
245
 
214
- cerver_request_t req;
215
- memset(&req, 0, sizeof(req));
246
+ cerver_request_t req;
247
+ memset(&req, 0, sizeof(req));
216
248
 
217
- if (cerver_parse_request(buf, req_len, &req) < 0) {
218
- const char *resp = "HTTP/1.1 400 Bad Request\r\n"
219
- "Content-Length: 11\r\nConnection: close\r\n\r\nBad Request";
220
- write(client_fd, resp, strlen(resp));
221
- free(buf);
222
- break;
223
- }
249
+ if (cerver_parse_request(buf, req_len, &req) < 0) {
250
+ const char* resp =
251
+ "HTTP/1.1 400 Bad Request\r\n"
252
+ "Content-Length: 11\r\nConnection: close\r\n\r\nBad Request";
253
+ write(client_fd, resp, strlen(resp));
254
+ free(buf);
255
+ break;
256
+ }
224
257
 
225
- request_count++;
226
- keepalive = !cerver_req_wants_close(&req);
258
+ request_count++;
259
+ keepalive = !cerver_req_wants_close(&req);
227
260
 
228
- cerver_response_t res;
229
- memset(&res, 0, sizeof(res));
261
+ cerver_response_t res;
262
+ memset(&res, 0, sizeof(res));
230
263
 
231
- if (cerver_serve_static(srv, &req, &res) < 0) {
232
- cerver_handler_fn handler = cerver_dispatch(srv, &req);
233
- if (handler) {
234
- handler(&req, &res);
235
- } else {
236
- cerver_res_text(&res, 404, "Not Found");
237
- }
238
- }
264
+ if (cerver_serve_static(srv, &req, &res) < 0) {
265
+ cerver_handler_fn handler = cerver_dispatch(srv, &req);
266
+ if (handler) {
267
+ handler(&req, &res);
268
+ } else {
269
+ cerver_res_text(&res, 404, "Not Found");
270
+ }
271
+ }
239
272
 
240
- if (res._force_close) keepalive = 0;
273
+ if (res._force_close) keepalive = 0;
241
274
 
242
- int write_err = cerver_write_response(client_fd, &res, keepalive);
275
+ int write_err = cerver_write_response(client_fd, &res, keepalive);
243
276
 
244
- if (res._body_owned == 1 && res.body) free((void *)res.body);
245
- else if (res._body_owned == 2 && res.body) munmap((void *)res.body, res.body_len);
277
+ if (res._body_owned == 1 && res.body)
278
+ free((void*)res.body);
279
+ else if (res._body_owned == 2 && res.body)
280
+ munmap((void*)res.body, res.body_len);
246
281
 
247
- free(buf);
248
- if (write_err < 0) break;
249
- }
282
+ free(buf);
283
+ if (write_err < 0) break;
284
+ }
250
285
 
251
- close(client_fd);
286
+ close(client_fd);
252
287
  }
253
288
 
254
289
  /* ------------------------------------------------------------------ */
255
290
  /* Connection pool worker thread */
256
291
  /* ------------------------------------------------------------------ */
257
292
 
258
- static void *conn_pool_worker(void *arg) {
259
- cerver_server_t *srv = (cerver_server_t *)arg;
293
+ static void* conn_pool_worker(void* arg) {
294
+ cerver_server_t* srv = (cerver_server_t*)arg;
295
+
296
+ /* Signal that this worker is ready */
297
+ pthread_mutex_lock(&g_worker_readiness.lock);
298
+ g_worker_readiness.workers_ready++;
299
+ pthread_cond_broadcast(&g_worker_readiness.ready_cv);
300
+ pthread_mutex_unlock(&g_worker_readiness.lock);
301
+
302
+ while (srv->running) {
303
+ int fd = cq_pop(&g_conn_queue, &srv->running);
304
+ if (fd < 0) continue;
305
+ handle_connection(srv, fd);
306
+ }
307
+ return NULL;
308
+ }
260
309
 
261
- while (srv->running) {
262
- int fd = cq_pop(&g_conn_queue, &srv->running);
263
- if (fd < 0) continue;
264
- handle_connection(srv, fd);
265
- }
266
- return NULL;
310
+ static int wait_for_pool_workers_ready(cerver_server_t* srv, int expected) {
311
+ pthread_mutex_lock(&g_worker_readiness.lock);
312
+ while (g_worker_readiness.workers_ready < expected && srv->running) {
313
+ pthread_cond_wait(&g_worker_readiness.ready_cv, &g_worker_readiness.lock);
314
+ }
315
+ int ready = (g_worker_readiness.workers_ready >= expected);
316
+ pthread_mutex_unlock(&g_worker_readiness.lock);
317
+ return ready;
318
+ }
319
+
320
+ static int wait_for_acceptors_ready(cerver_server_t* srv, int expected) {
321
+ pthread_mutex_lock(&g_acceptor_readiness.lock);
322
+ while (g_acceptor_readiness.acceptors_ready < expected && srv->running) {
323
+ pthread_cond_wait(&g_acceptor_readiness.ready_cv, &g_acceptor_readiness.lock);
324
+ }
325
+ int ready = (g_acceptor_readiness.acceptors_ready >= expected);
326
+ pthread_mutex_unlock(&g_acceptor_readiness.lock);
327
+ return ready;
328
+ }
329
+
330
+ static void release_acceptors(void) {
331
+ pthread_mutex_lock(&g_acceptor_readiness.lock);
332
+ g_acceptor_readiness.start_accepting = 1;
333
+ pthread_cond_broadcast(&g_acceptor_readiness.start_cv);
334
+ pthread_mutex_unlock(&g_acceptor_readiness.lock);
267
335
  }
268
336
 
269
337
  /* ------------------------------------------------------------------ */
@@ -271,34 +339,40 @@ static void *conn_pool_worker(void *arg) {
271
339
  /* ------------------------------------------------------------------ */
272
340
 
273
341
  static int create_listener(int port, int reuseport) {
274
- int fd = socket(AF_INET, SOCK_STREAM, 0);
275
- if (fd < 0) { perror("cerver: socket"); return -1; }
342
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
343
+ if (fd < 0) {
344
+ perror("cerver: socket");
345
+ return -1;
346
+ }
276
347
 
277
- int opt = 1;
278
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
348
+ int opt = 1;
349
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
279
350
 
280
351
  #ifdef __linux__
281
- if (reuseport)
282
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
352
+ if (reuseport) setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
283
353
  #else
284
- (void)reuseport;
354
+ (void)reuseport;
285
355
  #endif
286
356
 
287
- struct sockaddr_in addr;
288
- memset(&addr, 0, sizeof(addr));
289
- addr.sin_family = AF_INET;
290
- addr.sin_addr.s_addr = INADDR_ANY;
291
- addr.sin_port = htons((uint16_t)port);
357
+ struct sockaddr_in addr;
358
+ memset(&addr, 0, sizeof(addr));
359
+ addr.sin_family = AF_INET;
360
+ addr.sin_addr.s_addr = INADDR_ANY;
361
+ addr.sin_port = htons((uint16_t)port);
292
362
 
293
- if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
294
- perror("cerver: bind"); close(fd); return -1;
295
- }
296
- if (listen(fd, CERVER_LISTEN_BACKLOG) < 0) {
297
- perror("cerver: listen"); close(fd); return -1;
298
- }
363
+ if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
364
+ perror("cerver: bind");
365
+ close(fd);
366
+ return -1;
367
+ }
368
+ if (listen(fd, CERVER_LISTEN_BACKLOG) < 0) {
369
+ perror("cerver: listen");
370
+ close(fd);
371
+ return -1;
372
+ }
299
373
 
300
- set_nonblocking(fd);
301
- return fd;
374
+ set_nonblocking(fd);
375
+ return fd;
302
376
  }
303
377
 
304
378
  /* ------------------------------------------------------------------ */
@@ -306,12 +380,12 @@ static int create_listener(int port, int reuseport) {
306
380
  /* ------------------------------------------------------------------ */
307
381
 
308
382
  static int accept_connection(int listen_fd) {
309
- struct sockaddr_in ca;
310
- socklen_t cl = sizeof(ca);
383
+ struct sockaddr_in ca;
384
+ socklen_t cl = sizeof(ca);
311
385
  #ifdef __linux__
312
- return accept4(listen_fd, (struct sockaddr *)&ca, &cl, SOCK_CLOEXEC);
386
+ return accept4(listen_fd, (struct sockaddr*)&ca, &cl, SOCK_CLOEXEC);
313
387
  #else
314
- return accept(listen_fd, (struct sockaddr *)&ca, &cl);
388
+ return accept(listen_fd, (struct sockaddr*)&ca, &cl);
315
389
  #endif
316
390
  }
317
391
 
@@ -320,114 +394,155 @@ static int accept_connection(int listen_fd) {
320
394
  /* ------------------------------------------------------------------ */
321
395
 
322
396
  #if CERVER_USE_KQUEUE
323
- static void *acceptor_loop(void *arg) {
324
- cerver_worker_t *w = (cerver_worker_t *)arg;
325
- cerver_server_t *srv = w->srv;
326
-
327
- int kq = kqueue();
328
- if (kq < 0) { perror("cerver: kqueue"); return NULL; }
329
- w->event_fd = kq;
330
-
331
- struct kevent change;
332
- EV_SET(&change, w->listen_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
333
- kevent(kq, &change, 1, NULL, 0, NULL);
334
-
335
- struct kevent events[CERVER_MAX_EVENTS];
336
-
337
- while (srv->running) {
338
- struct timespec ts = { 1, 0 };
339
- int nev = kevent(kq, NULL, 0, events, CERVER_MAX_EVENTS, &ts);
340
- if (nev < 0) { if (errno == EINTR) continue; break; }
341
-
342
- for (int i = 0; i < nev; i++) {
343
- if ((int)events[i].ident == w->listen_fd) {
344
- while (1) {
345
- int cfd = accept_connection(w->listen_fd);
346
- if (cfd < 0) {
347
- if (errno == EAGAIN || errno == EWOULDBLOCK) break;
348
- break;
349
- }
350
- if (cq_push(&g_conn_queue, cfd) < 0) {
351
- /* Queue full send 503 and close */
352
- const char *r = "HTTP/1.1 503 Service Unavailable\r\n"
353
- "Content-Length: 19\r\nConnection: close\r\n\r\n"
354
- "Service Unavailable";
355
- write(cfd, r, strlen(r));
356
- close(cfd);
357
- }
358
- }
359
- }
397
+ static void* acceptor_loop(void* arg) {
398
+ cerver_worker_t* w = (cerver_worker_t*)arg;
399
+ cerver_server_t* srv = w->srv;
400
+
401
+ int kq = kqueue();
402
+ if (kq < 0) {
403
+ perror("cerver: kqueue");
404
+ return NULL;
405
+ }
406
+ w->event_fd = kq;
407
+
408
+ struct kevent change;
409
+ EV_SET(&change, w->listen_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
410
+ kevent(kq, &change, 1, NULL, 0, NULL);
411
+
412
+ struct kevent events[CERVER_MAX_EVENTS];
413
+
414
+ pthread_mutex_lock(&g_acceptor_readiness.lock);
415
+ g_acceptor_readiness.acceptors_ready++;
416
+ pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
417
+ while (!g_acceptor_readiness.start_accepting && srv->running) {
418
+ pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
419
+ }
420
+ pthread_mutex_unlock(&g_acceptor_readiness.lock);
421
+
422
+ while (srv->running) {
423
+ struct timespec ts = {1, 0};
424
+ int nev = kevent(kq, NULL, 0, events, CERVER_MAX_EVENTS, &ts);
425
+ if (nev < 0) {
426
+ if (errno == EINTR) continue;
427
+ break;
428
+ }
429
+
430
+ for (int i = 0; i < nev; i++) {
431
+ if ((int)events[i].ident == w->listen_fd) {
432
+ while (1) {
433
+ int cfd = accept_connection(w->listen_fd);
434
+ if (cfd < 0) {
435
+ if (errno == EAGAIN || errno == EWOULDBLOCK) break;
436
+ break;
437
+ }
438
+ if (cq_push(&g_conn_queue, cfd) < 0) {
439
+ /* Queue full — send 503 and close */
440
+ const char* r =
441
+ "HTTP/1.1 503 Service Unavailable\r\n"
442
+ "Content-Length: 19\r\nConnection: close\r\n\r\n"
443
+ "Service Unavailable";
444
+ write(cfd, r, strlen(r));
445
+ close(cfd);
446
+ }
360
447
  }
448
+ }
361
449
  }
450
+ }
362
451
 
363
- close(kq);
364
- return NULL;
452
+ close(kq);
453
+ return NULL;
365
454
  }
366
455
 
367
456
  #elif CERVER_USE_EPOLL
368
- static void *acceptor_loop(void *arg) {
369
- cerver_worker_t *w = (cerver_worker_t *)arg;
370
- cerver_server_t *srv = w->srv;
371
-
372
- int ep = epoll_create1(EPOLL_CLOEXEC);
373
- if (ep < 0) { perror("cerver: epoll"); return NULL; }
374
- w->event_fd = ep;
375
-
376
- struct epoll_event ev = { .events = EPOLLIN, .data.fd = w->listen_fd };
377
- epoll_ctl(ep, EPOLL_CTL_ADD, w->listen_fd, &ev);
378
-
379
- struct epoll_event events[CERVER_MAX_EVENTS];
380
-
381
- while (srv->running) {
382
- int nev = epoll_wait(ep, events, CERVER_MAX_EVENTS, 1000);
383
- if (nev < 0) { if (errno == EINTR) continue; break; }
384
-
385
- for (int i = 0; i < nev; i++) {
386
- if (events[i].data.fd == w->listen_fd) {
387
- while (1) {
388
- int cfd = accept_connection(w->listen_fd);
389
- if (cfd < 0) {
390
- if (errno == EAGAIN || errno == EWOULDBLOCK) break;
391
- break;
392
- }
393
- if (cq_push(&g_conn_queue, cfd) < 0) {
394
- const char *r = "HTTP/1.1 503 Service Unavailable\r\n"
395
- "Content-Length: 19\r\nConnection: close\r\n\r\n"
396
- "Service Unavailable";
397
- write(cfd, r, strlen(r));
398
- close(cfd);
399
- }
400
- }
401
- }
457
+ static void* acceptor_loop(void* arg) {
458
+ cerver_worker_t* w = (cerver_worker_t*)arg;
459
+ cerver_server_t* srv = w->srv;
460
+
461
+ int ep = epoll_create1(EPOLL_CLOEXEC);
462
+ if (ep < 0) {
463
+ perror("cerver: epoll");
464
+ return NULL;
465
+ }
466
+ w->event_fd = ep;
467
+
468
+ struct epoll_event ev = {.events = EPOLLIN, .data.fd = w->listen_fd};
469
+ epoll_ctl(ep, EPOLL_CTL_ADD, w->listen_fd, &ev);
470
+
471
+ struct epoll_event events[CERVER_MAX_EVENTS];
472
+
473
+ pthread_mutex_lock(&g_acceptor_readiness.lock);
474
+ g_acceptor_readiness.acceptors_ready++;
475
+ pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
476
+ while (!g_acceptor_readiness.start_accepting && srv->running) {
477
+ pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
478
+ }
479
+ pthread_mutex_unlock(&g_acceptor_readiness.lock);
480
+
481
+ while (srv->running) {
482
+ int nev = epoll_wait(ep, events, CERVER_MAX_EVENTS, 1000);
483
+ if (nev < 0) {
484
+ if (errno == EINTR) continue;
485
+ break;
486
+ }
487
+
488
+ for (int i = 0; i < nev; i++) {
489
+ if (events[i].data.fd == w->listen_fd) {
490
+ while (1) {
491
+ int cfd = accept_connection(w->listen_fd);
492
+ if (cfd < 0) {
493
+ if (errno == EAGAIN || errno == EWOULDBLOCK) break;
494
+ break;
495
+ }
496
+ if (cq_push(&g_conn_queue, cfd) < 0) {
497
+ const char* r =
498
+ "HTTP/1.1 503 Service Unavailable\r\n"
499
+ "Content-Length: 19\r\nConnection: close\r\n\r\n"
500
+ "Service Unavailable";
501
+ write(cfd, r, strlen(r));
502
+ close(cfd);
503
+ }
402
504
  }
505
+ }
403
506
  }
507
+ }
404
508
 
405
- close(ep);
406
- return NULL;
509
+ close(ep);
510
+ return NULL;
407
511
  }
408
512
 
409
513
  #else /* SELECT */
410
- static void *acceptor_loop(void *arg) {
411
- cerver_worker_t *w = (cerver_worker_t *)arg;
412
- cerver_server_t *srv = w->srv;
413
-
414
- while (srv->running) {
415
- fd_set rfds;
416
- FD_ZERO(&rfds);
417
- FD_SET(w->listen_fd, &rfds);
418
- struct timeval tv = { 1, 0 };
419
- int ret = select(w->listen_fd + 1, &rfds, NULL, NULL, &tv);
420
- if (ret < 0) { if (errno == EINTR) continue; break; }
421
- if (ret > 0 && FD_ISSET(w->listen_fd, &rfds)) {
422
- int cfd = accept_connection(w->listen_fd);
423
- if (cfd >= 0) {
424
- if (cq_push(&g_conn_queue, cfd) < 0) {
425
- close(cfd);
426
- }
427
- }
514
+ static void* acceptor_loop(void* arg) {
515
+ cerver_worker_t* w = (cerver_worker_t*)arg;
516
+ cerver_server_t* srv = w->srv;
517
+
518
+ pthread_mutex_lock(&g_acceptor_readiness.lock);
519
+ g_acceptor_readiness.acceptors_ready++;
520
+ pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
521
+ while (!g_acceptor_readiness.start_accepting && srv->running) {
522
+ pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
523
+ }
524
+ pthread_mutex_unlock(&g_acceptor_readiness.lock);
525
+
526
+ while (srv->running) {
527
+ fd_set rfds;
528
+ FD_ZERO(&rfds);
529
+ FD_SET(w->listen_fd, &rfds);
530
+ struct timeval tv = {1, 0};
531
+ int ret = select(w->listen_fd + 1, &rfds, NULL, NULL, &tv);
532
+ if (ret < 0) {
533
+ if (errno == EINTR) continue;
534
+ break;
535
+ }
536
+ if (ret > 0 && FD_ISSET(w->listen_fd, &rfds)) {
537
+ int cfd = accept_connection(w->listen_fd);
538
+ if (cfd >= 0) {
539
+ if (cq_push(&g_conn_queue, cfd) < 0) {
540
+ close(cfd);
428
541
  }
542
+ }
429
543
  }
430
- return NULL;
544
+ }
545
+ return NULL;
431
546
  }
432
547
  #endif
433
548
 
@@ -435,221 +550,264 @@ static void *acceptor_loop(void *arg) {
435
550
  /* Stat cache */
436
551
  /* ------------------------------------------------------------------ */
437
552
 
438
- void cerver_stat_cache_init(cerver_stat_cache_t *cache) {
439
- memset(cache, 0, sizeof(*cache));
440
- pthread_mutex_init(&cache->lock, NULL);
553
+ void cerver_stat_cache_init(cerver_stat_cache_t* cache) {
554
+ memset(cache, 0, sizeof(*cache));
555
+ pthread_mutex_init(&cache->lock, NULL);
441
556
  }
442
557
 
443
- int cerver_stat_cache_lookup(cerver_stat_cache_t *cache, const char *path,
444
- size_t *file_size) {
445
- time_t now = time(NULL);
446
- pthread_mutex_lock(&cache->lock);
447
- for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
448
- cerver_stat_entry_t *e = &cache->entries[i];
449
- if (e->valid && strcmp(e->path, path) == 0) {
450
- if (now - e->cached_at < CERVER_STAT_CACHE_TTL) {
451
- *file_size = e->file_size;
452
- pthread_mutex_unlock(&cache->lock);
453
- return 0;
454
- }
455
- e->valid = 0;
456
- break;
457
- }
558
+ int cerver_stat_cache_lookup(cerver_stat_cache_t* cache, const char* path, size_t* file_size) {
559
+ time_t now = time(NULL);
560
+ pthread_mutex_lock(&cache->lock);
561
+ for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
562
+ cerver_stat_entry_t* e = &cache->entries[i];
563
+ if (e->valid && strcmp(e->path, path) == 0) {
564
+ if (now - e->cached_at < CERVER_STAT_CACHE_TTL) {
565
+ *file_size = e->file_size;
566
+ pthread_mutex_unlock(&cache->lock);
567
+ return 0;
568
+ }
569
+ e->valid = 0;
570
+ break;
458
571
  }
459
- pthread_mutex_unlock(&cache->lock);
460
- return -1;
572
+ }
573
+ pthread_mutex_unlock(&cache->lock);
574
+ return -1;
461
575
  }
462
576
 
463
- void cerver_stat_cache_store(cerver_stat_cache_t *cache, const char *path,
464
- size_t file_size, time_t mtime) {
465
- time_t now = time(NULL);
466
- pthread_mutex_lock(&cache->lock);
467
- int best = 0;
468
- time_t oldest = cache->entries[0].cached_at;
469
- for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
470
- cerver_stat_entry_t *e = &cache->entries[i];
471
- if (!e->valid) { best = i; break; }
472
- if (e->cached_at < oldest) { oldest = e->cached_at; best = i; }
577
+ void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_t file_size,
578
+ time_t mtime) {
579
+ time_t now = time(NULL);
580
+ pthread_mutex_lock(&cache->lock);
581
+ int best = 0;
582
+ time_t oldest = cache->entries[0].cached_at;
583
+ for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
584
+ cerver_stat_entry_t* e = &cache->entries[i];
585
+ if (!e->valid) {
586
+ best = i;
587
+ break;
588
+ }
589
+ if (e->cached_at < oldest) {
590
+ oldest = e->cached_at;
591
+ best = i;
473
592
  }
474
- cerver_stat_entry_t *slot = &cache->entries[best];
475
- strncpy(slot->path, path, sizeof(slot->path) - 1);
476
- slot->path[sizeof(slot->path) - 1] = '\0';
477
- slot->file_size = file_size;
478
- slot->mtime = mtime;
479
- slot->cached_at = now;
480
- slot->valid = 1;
481
- pthread_mutex_unlock(&cache->lock);
593
+ }
594
+ cerver_stat_entry_t* slot = &cache->entries[best];
595
+ strncpy(slot->path, path, sizeof(slot->path) - 1);
596
+ slot->path[sizeof(slot->path) - 1] = '\0';
597
+ slot->file_size = file_size;
598
+ slot->mtime = mtime;
599
+ slot->cached_at = now;
600
+ slot->valid = 1;
601
+ pthread_mutex_unlock(&cache->lock);
482
602
  }
483
603
 
484
604
  /* ------------------------------------------------------------------ */
485
605
  /* Server init */
486
606
  /* ------------------------------------------------------------------ */
487
607
 
488
- int cerver_init(cerver_server_t *srv, int port, int threads) {
489
- memset(srv, 0, sizeof(*srv));
490
- srv->port = port;
491
- srv->sock_fd = -1;
492
- srv->running = 0;
493
- srv->public_dir = NULL;
494
- srv->dispatch_override = NULL;
495
- srv->worker_count = (threads > 0) ? threads : get_cpu_count();
496
- srv->workers = NULL;
497
- cerver_stat_cache_init(&srv->stat_cache);
498
- return 0;
608
+ int cerver_init(cerver_server_t* srv, int port, int threads) {
609
+ memset(srv, 0, sizeof(*srv));
610
+ srv->port = port;
611
+ srv->sock_fd = -1;
612
+ srv->running = 0;
613
+ srv->public_dir = NULL;
614
+ srv->dispatch_override = NULL;
615
+ srv->worker_count = (threads > 0) ? threads : get_cpu_count();
616
+ srv->workers = NULL;
617
+ cerver_stat_cache_init(&srv->stat_cache);
618
+ return 0;
499
619
  }
500
620
 
501
- int cerver_add_routes(cerver_server_t *srv, cerver_route_t *routes, int count) {
502
- srv->routes = routes;
503
- srv->route_count = count;
504
- return 0;
621
+ int cerver_add_routes(cerver_server_t* srv, cerver_route_t* routes, int count) {
622
+ srv->routes = routes;
623
+ srv->route_count = count;
624
+ return 0;
505
625
  }
506
626
 
507
- int cerver_set_assets(cerver_server_t *srv, cerver_asset_t *assets, int count) {
508
- srv->assets = assets;
509
- srv->asset_count = count;
510
- return 0;
627
+ int cerver_set_assets(cerver_server_t* srv, cerver_asset_t* assets, int count) {
628
+ srv->assets = assets;
629
+ srv->asset_count = count;
630
+ return 0;
511
631
  }
512
632
 
513
- void cerver_set_public_dir(cerver_server_t *srv, const char *dir) {
514
- srv->public_dir = dir;
515
- }
633
+ void cerver_set_public_dir(cerver_server_t* srv, const char* dir) { srv->public_dir = dir; }
516
634
 
517
635
  /* ------------------------------------------------------------------ */
518
636
  /* Server listen */
519
637
  /* ------------------------------------------------------------------ */
520
638
 
521
- int cerver_listen(cerver_server_t *srv) {
522
- srv->sock_fd = create_listener(srv->port, 0);
523
- if (srv->sock_fd < 0) return -1;
524
-
525
- g_srv = srv;
526
- signal(SIGINT, signal_handler);
527
- signal(SIGTERM, signal_handler);
528
- signal(SIGPIPE, SIG_IGN);
529
-
530
- srv->running = 1;
531
-
532
- /* Determine pool and acceptor counts.
533
- * Acceptors: min(worker_count, cpu_count) — one per core for accept.
534
- * Pool workers: worker_count * 16 — enough to cover concurrent keep-alive. */
535
- int cpu_count = get_cpu_count();
536
- int acceptor_count = cpu_count;
537
- if (acceptor_count > srv->worker_count) acceptor_count = srv->worker_count;
538
- if (acceptor_count < 1) acceptor_count = 1;
539
-
540
- int pool_size = srv->worker_count * 16;
541
- if (pool_size < CERVER_CONN_POOL_SIZE) pool_size = CERVER_CONN_POOL_SIZE;
542
- if (pool_size > 1024) pool_size = 1024;
543
-
544
- printf("cerver: listening on http://localhost:%d\n", srv->port);
545
- printf("cerver: %d acceptor(s), %d connection workers, keep-alive max %d req/conn\n",
546
- acceptor_count, pool_size, CERVER_KEEPALIVE_MAX);
547
-
548
- /* Init shared connection queue */
549
- cq_init(&g_conn_queue);
550
-
551
- /* Start connection pool workers */
552
- pthread_t *pool_threads = calloc((size_t)pool_size, sizeof(pthread_t));
553
- if (!pool_threads) { perror("cerver: calloc pool"); close(srv->sock_fd); return -1; }
554
-
555
- pthread_attr_t attr;
556
- pthread_attr_init(&attr);
557
- pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
558
-
559
- for (int i = 0; i < pool_size; i++) {
560
- if (pthread_create(&pool_threads[i], &attr, conn_pool_worker, srv) != 0) {
561
- perror("cerver: pool thread create");
562
- srv->running = 0;
563
- for (int j = 0; j < i; j++) pthread_join(pool_threads[j], NULL);
564
- free(pool_threads);
565
- close(srv->sock_fd);
566
- pthread_attr_destroy(&attr);
567
- return -1;
568
- }
639
+ int cerver_listen(cerver_server_t* srv) {
640
+ g_srv = srv;
641
+ signal(SIGINT, signal_handler);
642
+ signal(SIGTERM, signal_handler);
643
+ signal(SIGPIPE, SIG_IGN);
644
+
645
+ srv->running = 1;
646
+
647
+ /* Determine pool and acceptor counts.
648
+ * Acceptors: min(worker_count, cpu_count) — one per core for accept.
649
+ * Pool workers: worker_count * 16 — enough to cover concurrent keep-alive. */
650
+ int cpu_count = get_cpu_count();
651
+ int acceptor_count = cpu_count;
652
+ if (acceptor_count > srv->worker_count) acceptor_count = srv->worker_count;
653
+ if (acceptor_count < 1) acceptor_count = 1;
654
+
655
+ int pool_size = srv->worker_count * 16;
656
+ if (pool_size < CERVER_CONN_POOL_SIZE) pool_size = CERVER_CONN_POOL_SIZE;
657
+ if (pool_size > 1024) pool_size = 1024;
658
+
659
+ /* Init shared connection queue */
660
+ cq_init(&g_conn_queue);
661
+
662
+ /* Initialize worker readiness tracking */
663
+ pthread_mutex_lock(&g_worker_readiness.lock);
664
+ g_worker_readiness.workers_ready = 0;
665
+ g_worker_readiness.workers_expected = pool_size;
666
+ pthread_mutex_unlock(&g_worker_readiness.lock);
667
+
668
+ pthread_mutex_lock(&g_acceptor_readiness.lock);
669
+ g_acceptor_readiness.acceptors_ready = 0;
670
+ g_acceptor_readiness.start_accepting = 0;
671
+ pthread_mutex_unlock(&g_acceptor_readiness.lock);
672
+
673
+ /* Start connection pool workers */
674
+ pthread_t* pool_threads = calloc((size_t)pool_size, sizeof(pthread_t));
675
+ if (!pool_threads) {
676
+ perror("cerver: calloc pool");
677
+ if (srv->sock_fd >= 0) close(srv->sock_fd);
678
+ return -1;
679
+ }
680
+
681
+ pthread_attr_t attr;
682
+ pthread_attr_init(&attr);
683
+ pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
684
+
685
+ for (int i = 0; i < pool_size; i++) {
686
+ if (pthread_create(&pool_threads[i], &attr, conn_pool_worker, srv) != 0) {
687
+ perror("cerver: pool thread create");
688
+ srv->running = 0;
689
+ for (int j = 0; j < i; j++) pthread_join(pool_threads[j], NULL);
690
+ free(pool_threads);
691
+ if (srv->sock_fd >= 0) close(srv->sock_fd);
692
+ pthread_attr_destroy(&attr);
693
+ return -1;
569
694
  }
695
+ }
570
696
 
571
- /* Start acceptor threads */
572
- srv->workers = calloc((size_t)acceptor_count, sizeof(cerver_worker_t));
573
- srv->worker_count = acceptor_count;
574
- if (!srv->workers) {
575
- perror("cerver: calloc acceptors");
576
- srv->running = 0;
577
- pthread_cond_broadcast(&g_conn_queue.not_empty);
578
- for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
579
- free(pool_threads);
580
- close(srv->sock_fd);
581
- pthread_attr_destroy(&attr);
582
- return -1;
583
- }
697
+ if (!wait_for_pool_workers_ready(srv, pool_size)) {
698
+ srv->running = 0;
699
+ for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
700
+ free(pool_threads);
701
+ pthread_attr_destroy(&attr);
702
+ return -1;
703
+ }
584
704
 
585
- for (int i = 0; i < acceptor_count; i++) {
586
- cerver_worker_t *w = &srv->workers[i];
587
- w->id = i;
588
- w->srv = srv;
589
- w->event_fd = -1;
705
+ srv->sock_fd = create_listener(srv->port, 0);
706
+ if (srv->sock_fd < 0) {
707
+ srv->running = 0;
708
+ for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
709
+ free(pool_threads);
710
+ pthread_attr_destroy(&attr);
711
+ return -1;
712
+ }
713
+
714
+ /* Start acceptor threads */
715
+ srv->workers = calloc((size_t)acceptor_count, sizeof(cerver_worker_t));
716
+ srv->worker_count = acceptor_count;
717
+ if (!srv->workers) {
718
+ perror("cerver: calloc acceptors");
719
+ srv->running = 0;
720
+ pthread_cond_broadcast(&g_conn_queue.not_empty);
721
+ for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
722
+ free(pool_threads);
723
+ close(srv->sock_fd);
724
+ pthread_attr_destroy(&attr);
725
+ return -1;
726
+ }
727
+
728
+ for (int i = 0; i < acceptor_count; i++) {
729
+ cerver_worker_t* w = &srv->workers[i];
730
+ w->id = i;
731
+ w->srv = srv;
732
+ w->event_fd = -1;
590
733
  #ifdef __linux__
591
- w->listen_fd = create_listener(srv->port, 1);
592
- if (w->listen_fd < 0) w->listen_fd = srv->sock_fd;
734
+ w->listen_fd = create_listener(srv->port, 1);
735
+ if (w->listen_fd < 0) w->listen_fd = srv->sock_fd;
593
736
  #else
594
- w->listen_fd = srv->sock_fd;
737
+ w->listen_fd = srv->sock_fd;
595
738
  #endif
596
- if (pthread_create(&w->thread, &attr, acceptor_loop, w) != 0) {
597
- perror("cerver: acceptor create");
598
- srv->running = 0;
599
- for (int j = 0; j < i; j++) pthread_join(srv->workers[j].thread, NULL);
600
- break;
601
- }
739
+ if (pthread_create(&w->thread, &attr, acceptor_loop, w) != 0) {
740
+ perror("cerver: acceptor create");
741
+ srv->running = 0;
742
+ for (int j = 0; j < i; j++) pthread_join(srv->workers[j].thread, NULL);
743
+ break;
602
744
  }
745
+ }
603
746
 
747
+ if (!wait_for_acceptors_ready(srv, acceptor_count)) {
748
+ srv->running = 0;
749
+ release_acceptors();
750
+ for (int i = 0; i < acceptor_count; i++) pthread_join(srv->workers[i].thread, NULL);
751
+ for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
752
+ free(pool_threads);
604
753
  pthread_attr_destroy(&attr);
754
+ return -1;
755
+ }
605
756
 
606
- /* Main thread waits for shutdown */
607
- while (srv->running) sleep(1);
757
+ printf("cerver: listening on http://localhost:%d\n", srv->port);
758
+ printf("cerver: %d acceptor(s), %d connection workers, keep-alive max %d req/conn\n",
759
+ acceptor_count, pool_size, CERVER_KEEPALIVE_MAX);
608
760
 
609
- /* Shutdown: stop acceptors first, then drain pool */
610
- srv->running = 0;
761
+ release_acceptors();
611
762
 
612
- for (int i = 0; i < acceptor_count; i++)
613
- pthread_join(srv->workers[i].thread, NULL);
763
+ pthread_attr_destroy(&attr);
614
764
 
615
- /* Wake all pool workers */
616
- pthread_mutex_lock(&g_conn_queue.lock);
617
- pthread_cond_broadcast(&g_conn_queue.not_empty);
618
- pthread_mutex_unlock(&g_conn_queue.lock);
765
+ /* Main thread waits for shutdown */
766
+ while (srv->running) sleep(1);
619
767
 
620
- for (int i = 0; i < pool_size; i++)
621
- pthread_join(pool_threads[i], NULL);
622
- free(pool_threads);
768
+ /* Shutdown: stop acceptors first, then drain pool */
769
+ srv->running = 0;
770
+
771
+ for (int i = 0; i < acceptor_count; i++) pthread_join(srv->workers[i].thread, NULL);
772
+
773
+ /* Wake all pool workers */
774
+ pthread_mutex_lock(&g_conn_queue.lock);
775
+ pthread_cond_broadcast(&g_conn_queue.not_empty);
776
+ pthread_mutex_unlock(&g_conn_queue.lock);
623
777
 
624
- cerver_shutdown(srv);
625
- return 0;
778
+ for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
779
+ free(pool_threads);
780
+
781
+ cerver_shutdown(srv);
782
+ return 0;
626
783
  }
627
784
 
628
785
  /* ------------------------------------------------------------------ */
629
786
  /* Shutdown */
630
787
  /* ------------------------------------------------------------------ */
631
788
 
632
- void cerver_shutdown(cerver_server_t *srv) {
633
- srv->running = 0;
789
+ void cerver_shutdown(cerver_server_t* srv) {
790
+ srv->running = 0;
634
791
 
635
- if (srv->workers) {
636
- for (int i = 0; i < srv->worker_count; i++) {
792
+ if (srv->workers) {
793
+ for (int i = 0; i < srv->worker_count; i++) {
637
794
  #ifdef __linux__
638
- if (srv->workers[i].listen_fd != srv->sock_fd &&
639
- srv->workers[i].listen_fd >= 0)
640
- close(srv->workers[i].listen_fd);
795
+ if (srv->workers[i].listen_fd != srv->sock_fd && srv->workers[i].listen_fd >= 0)
796
+ close(srv->workers[i].listen_fd);
641
797
  #endif
642
- if (srv->workers[i].event_fd >= 0)
643
- close(srv->workers[i].event_fd);
644
- }
645
- free(srv->workers);
646
- srv->workers = NULL;
798
+ if (srv->workers[i].event_fd >= 0) close(srv->workers[i].event_fd);
647
799
  }
800
+ free(srv->workers);
801
+ srv->workers = NULL;
802
+ }
648
803
 
649
- if (srv->sock_fd >= 0) { close(srv->sock_fd); srv->sock_fd = -1; }
804
+ if (srv->sock_fd >= 0) {
805
+ close(srv->sock_fd);
806
+ srv->sock_fd = -1;
807
+ }
650
808
 
651
- cq_destroy(&g_conn_queue);
652
- pthread_mutex_destroy(&srv->stat_cache.lock);
809
+ cq_destroy(&g_conn_queue);
810
+ pthread_mutex_destroy(&srv->stat_cache.lock);
653
811
 
654
- printf("\ncerver: server stopped\n");
812
+ printf("\ncerver: server stopped\n");
655
813
  }