@velox0/cerver 0.6.2 → 0.6.3

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
@@ -1,16 +1,16 @@
1
1
  /*
2
- * server.c — Hybrid event-loop + thread-pool server for the cerver runtime.
2
+ * server.c — Hybrid event-loop + connection-worker server for the cerver runtime.
3
3
  *
4
4
  * Cross-platform: Linux (epoll), macOS/BSD (kqueue), Windows (select/IOCP-ready),
5
5
  * and any other POSIX platform (select fallback).
6
6
  *
7
7
  * Architecture:
8
8
  * - 1 acceptor thread per core with its own kqueue/epoll (accept only)
9
- * - Shared connection thread pool (configurable size, default 128)
9
+ * - Shared connection worker pool (configurable size, default 128)
10
10
  * - Acceptors never block — they push fds into a shared queue guarded by a mutex/condvar
11
- * - Pool workers handle full request lifecycle including keep-alive
11
+ * - Connection workers handle full request lifecycle including keep-alive
12
12
  * - On Linux: SO_REUSEPORT per acceptor; on macOS: shared listener
13
- * - On Windows: Winsock2, select-based acceptor, pthreads-win32
13
+ * - On Windows: Winsock2, select-based acceptor, native Windows threads
14
14
  */
15
15
 
16
16
  #include "win_compat.h" /* Must come first on Windows */
@@ -22,7 +22,6 @@
22
22
  #include <errno.h>
23
23
  #include <signal.h>
24
24
  #include <time.h>
25
- #include <pthread.h>
26
25
 
27
26
  #if !CERVER_PLATFORM_WINDOWS
28
27
  #include <unistd.h>
@@ -56,9 +55,9 @@
56
55
  #include <sched.h>
57
56
  #endif // __linux__
58
57
 
59
- /* Connection pool sizing */
60
- #define CERVER_CONN_POOL_SIZE 128
61
- #define CERVER_CONN_QUEUE_SIZE 4096
58
+ /* Connection worker sizing */
59
+ #define CERVER_CONNECTION_WORKER_POOL_MIN 128
60
+ #define CERVER_CONN_QUEUE_SIZE 4096
62
61
 
63
62
  /* ------------------------------------------------------------------ */
64
63
  /* memmem fallback (also defined in win_compat.h for Windows) */
@@ -117,88 +116,88 @@ static void best_effort_write(cerver_sock_t fd, const char* buf, size_t len) {
117
116
  static void cerver_platform_close(cerver_sock_t fd) { cerver_close_sock(fd); }
118
117
 
119
118
  /* ------------------------------------------------------------------ */
120
- /* Connection queue (shared between acceptors and pool workers) */
119
+ /* Connection queue (shared between acceptors and connection workers) */
121
120
  /* ------------------------------------------------------------------ */
122
121
 
123
122
  typedef struct {
124
- cerver_sock_t fds[CERVER_CONN_QUEUE_SIZE];
125
- int head;
126
- int tail;
127
- int count;
128
- pthread_mutex_t lock;
129
- pthread_cond_t not_empty;
123
+ cerver_sock_t fds[CERVER_CONN_QUEUE_SIZE];
124
+ int head;
125
+ int tail;
126
+ int count;
127
+ cerver_mutex_t lock;
128
+ cerver_cond_t not_empty;
130
129
  } conn_queue_t;
131
130
 
132
131
  static void cq_init(conn_queue_t* q) {
133
132
  for (int i = 0; i < CERVER_CONN_QUEUE_SIZE; i++) q->fds[i] = CERVER_INVALID_SOCK;
134
133
  q->head = q->tail = q->count = 0;
135
- pthread_mutex_init(&q->lock, NULL);
136
- pthread_cond_init(&q->not_empty, NULL);
134
+ cerver_mutex_init(&q->lock, NULL);
135
+ cerver_cond_init(&q->not_empty, NULL);
137
136
  }
138
137
 
139
138
  static void cq_destroy(conn_queue_t* q) {
140
- pthread_mutex_destroy(&q->lock);
141
- pthread_cond_destroy(&q->not_empty);
139
+ cerver_mutex_destroy(&q->lock);
140
+ cerver_cond_destroy(&q->not_empty);
142
141
  }
143
142
 
144
143
  /* Returns 0 on success, -1 if queue is full. */
145
144
  static int cq_push(conn_queue_t* q, cerver_sock_t fd) {
146
- pthread_mutex_lock(&q->lock);
145
+ cerver_mutex_lock(&q->lock);
147
146
  if (q->count >= CERVER_CONN_QUEUE_SIZE) {
148
- pthread_mutex_unlock(&q->lock);
147
+ cerver_mutex_unlock(&q->lock);
149
148
  return -1;
150
149
  }
151
150
  q->fds[q->tail] = fd;
152
151
  q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
153
152
  q->count++;
154
- pthread_cond_signal(&q->not_empty);
155
- pthread_mutex_unlock(&q->lock);
153
+ cerver_cond_signal(&q->not_empty);
154
+ cerver_mutex_unlock(&q->lock);
156
155
  return 0;
157
156
  }
158
157
 
159
158
  /* Returns fd, or CERVER_INVALID_SOCK on shutdown. */
160
159
  static cerver_sock_t cq_pop(conn_queue_t* q, volatile int* running) {
161
- pthread_mutex_lock(&q->lock);
160
+ cerver_mutex_lock(&q->lock);
162
161
  while (q->count == 0 && *running) {
163
162
  struct timespec ts;
164
163
  clock_gettime(CLOCK_REALTIME, &ts);
165
164
  ts.tv_sec += 1;
166
- pthread_cond_timedwait(&q->not_empty, &q->lock, &ts);
165
+ cerver_cond_timedwait(&q->not_empty, &q->lock, &ts);
167
166
  }
168
167
  if (q->count == 0) {
169
- pthread_mutex_unlock(&q->lock);
168
+ cerver_mutex_unlock(&q->lock);
170
169
  return CERVER_INVALID_SOCK;
171
170
  }
172
171
  cerver_sock_t fd = q->fds[q->head];
173
172
  q->head = (q->head + 1) % CERVER_CONN_QUEUE_SIZE;
174
173
  q->count--;
175
- pthread_mutex_unlock(&q->lock);
174
+ cerver_mutex_unlock(&q->lock);
176
175
  return fd;
177
176
  }
178
177
 
179
178
  static conn_queue_t g_conn_queue;
180
179
 
181
- /* Worker / acceptor readiness tracking */
180
+ /* Connection worker / acceptor readiness tracking */
182
181
  typedef struct {
183
- int workers_ready;
184
- int workers_expected;
185
- pthread_mutex_t lock;
186
- pthread_cond_t ready_cv;
187
- } worker_readiness_t;
182
+ int connection_workers_ready;
183
+ int connection_workers_expected;
184
+ cerver_mutex_t lock;
185
+ cerver_cond_t ready_cv;
186
+ } connection_worker_readiness_t;
188
187
 
189
- static worker_readiness_t g_worker_readiness = {0, 0, PTHREAD_MUTEX_INITIALIZER,
190
- PTHREAD_COND_INITIALIZER};
188
+ static connection_worker_readiness_t g_connection_worker_readiness = {
189
+ 0, 0, CERVER_MUTEX_INITIALIZER, CERVER_COND_INITIALIZER};
191
190
 
192
191
  typedef struct {
193
- int acceptors_ready;
194
- int start_accepting;
195
- pthread_mutex_t lock;
196
- pthread_cond_t ready_cv;
197
- pthread_cond_t start_cv;
192
+ int acceptors_ready;
193
+ int start_accepting;
194
+ cerver_mutex_t lock;
195
+ cerver_cond_t ready_cv;
196
+ cerver_cond_t start_cv;
198
197
  } acceptor_readiness_t;
199
198
 
200
199
  static acceptor_readiness_t g_acceptor_readiness = {
201
- 0, 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER};
200
+ 0, 0, CERVER_MUTEX_INITIALIZER, CERVER_COND_INITIALIZER, CERVER_COND_INITIALIZER};
202
201
 
203
202
  /* ------------------------------------------------------------------ */
204
203
  /* Buffered read */
@@ -210,6 +209,11 @@ static char* read_full_request(cerver_sock_t fd, size_t* out_len) {
210
209
  char* buf = malloc(cap + 1);
211
210
  if (!buf) return NULL;
212
211
 
212
+ #if CERVER_PLATFORM_WINDOWS
213
+ /* On Windows set a blocking recv timeout via SO_RCVTIMEO (already set
214
+ per-connection in handle_connection). Just read normally. */
215
+ #endif // CERVER_PLATFORM_WINDOWS
216
+
213
217
  while (len < (size_t)CERVER_READ_BUF_MAX) {
214
218
  ssize_t n = (ssize_t)cerver_sock_read(fd, buf + len, cap - len);
215
219
  if (n <= 0) break;
@@ -327,16 +331,16 @@ static void handle_connection(cerver_server_t* srv, cerver_sock_t client_fd) {
327
331
  }
328
332
 
329
333
  /* ------------------------------------------------------------------ */
330
- /* Connection pool worker thread */
334
+ /* Connection worker thread */
331
335
  /* ------------------------------------------------------------------ */
332
336
 
333
- static void* conn_pool_worker(void* arg) {
337
+ static void* connection_worker_loop(void* arg) {
334
338
  cerver_server_t* srv = (cerver_server_t*)arg;
335
339
 
336
- pthread_mutex_lock(&g_worker_readiness.lock);
337
- g_worker_readiness.workers_ready++;
338
- pthread_cond_broadcast(&g_worker_readiness.ready_cv);
339
- pthread_mutex_unlock(&g_worker_readiness.lock);
340
+ cerver_mutex_lock(&g_connection_worker_readiness.lock);
341
+ g_connection_worker_readiness.connection_workers_ready++;
342
+ cerver_cond_broadcast(&g_connection_worker_readiness.ready_cv);
343
+ cerver_mutex_unlock(&g_connection_worker_readiness.lock);
340
344
 
341
345
  while (srv->running) {
342
346
  cerver_sock_t fd = cq_pop(&g_conn_queue, &srv->running);
@@ -346,29 +350,29 @@ static void* conn_pool_worker(void* arg) {
346
350
  return NULL;
347
351
  }
348
352
 
349
- static int wait_for_pool_workers_ready(cerver_server_t* srv, int expected) {
350
- pthread_mutex_lock(&g_worker_readiness.lock);
351
- while (g_worker_readiness.workers_ready < expected && srv->running)
352
- pthread_cond_wait(&g_worker_readiness.ready_cv, &g_worker_readiness.lock);
353
- int ready = (g_worker_readiness.workers_ready >= expected);
354
- pthread_mutex_unlock(&g_worker_readiness.lock);
353
+ static int wait_for_connection_workers_ready(cerver_server_t* srv, int expected) {
354
+ cerver_mutex_lock(&g_connection_worker_readiness.lock);
355
+ while (g_connection_worker_readiness.connection_workers_ready < expected && srv->running)
356
+ cerver_cond_wait(&g_connection_worker_readiness.ready_cv, &g_connection_worker_readiness.lock);
357
+ int ready = (g_connection_worker_readiness.connection_workers_ready >= expected);
358
+ cerver_mutex_unlock(&g_connection_worker_readiness.lock);
355
359
  return ready;
356
360
  }
357
361
 
358
362
  static int wait_for_acceptors_ready(cerver_server_t* srv, int expected) {
359
- pthread_mutex_lock(&g_acceptor_readiness.lock);
363
+ cerver_mutex_lock(&g_acceptor_readiness.lock);
360
364
  while (g_acceptor_readiness.acceptors_ready < expected && srv->running)
361
- pthread_cond_wait(&g_acceptor_readiness.ready_cv, &g_acceptor_readiness.lock);
365
+ cerver_cond_wait(&g_acceptor_readiness.ready_cv, &g_acceptor_readiness.lock);
362
366
  int ready = (g_acceptor_readiness.acceptors_ready >= expected);
363
- pthread_mutex_unlock(&g_acceptor_readiness.lock);
367
+ cerver_mutex_unlock(&g_acceptor_readiness.lock);
364
368
  return ready;
365
369
  }
366
370
 
367
371
  static void release_acceptors(void) {
368
- pthread_mutex_lock(&g_acceptor_readiness.lock);
372
+ cerver_mutex_lock(&g_acceptor_readiness.lock);
369
373
  g_acceptor_readiness.start_accepting = 1;
370
- pthread_cond_broadcast(&g_acceptor_readiness.start_cv);
371
- pthread_mutex_unlock(&g_acceptor_readiness.lock);
374
+ cerver_cond_broadcast(&g_acceptor_readiness.start_cv);
375
+ cerver_mutex_unlock(&g_acceptor_readiness.lock);
372
376
  }
373
377
 
374
378
  /* ------------------------------------------------------------------ */
@@ -392,11 +396,11 @@ static cerver_sock_t create_listener(int port, int reuseport) {
392
396
  int opt = 1;
393
397
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, CERVER_SETSOCKOPT_CAST & opt, sizeof(opt));
394
398
 
395
- #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
399
+ #if defined(SO_REUSEPORT)
396
400
  if (reuseport) setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
397
401
  #else
398
402
  (void)reuseport;
399
- #endif // __linux__ || __APPLE__ || __FreeBSD__
403
+ #endif // SO_REUSEPORT
400
404
 
401
405
  struct sockaddr_in addr;
402
406
  memset(&addr, 0, sizeof(addr));
@@ -426,11 +430,16 @@ static cerver_sock_t create_listener(int port, int reuseport) {
426
430
  static cerver_sock_t accept_connection(cerver_sock_t listen_fd) {
427
431
  struct sockaddr_in ca;
428
432
  socklen_t cl = sizeof(ca);
429
- #if defined(__linux__)
430
- return accept4(listen_fd, (struct sockaddr*)&ca, &cl, SOCK_CLOEXEC);
431
- #else
432
- return accept(listen_fd, (struct sockaddr*)&ca, &cl);
433
- #endif // __linux__
433
+ cerver_sock_t fd = accept(listen_fd, (struct sockaddr*)&ca, &cl);
434
+ #if !CERVER_PLATFORM_WINDOWS
435
+ #if defined(FD_CLOEXEC)
436
+ if (fd >= 0) {
437
+ int flags = fcntl(fd, F_GETFD, 0);
438
+ if (flags >= 0) fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
439
+ }
440
+ #endif // FD_CLOEXEC
441
+ #endif // !CERVER_PLATFORM_WINDOWS
442
+ return fd;
434
443
  }
435
444
 
436
445
  /* ------------------------------------------------------------------ */
@@ -440,28 +449,28 @@ static cerver_sock_t accept_connection(cerver_sock_t listen_fd) {
440
449
  #if defined(CERVER_USE_KQUEUE)
441
450
 
442
451
  static void* acceptor_loop(void* arg) {
443
- cerver_worker_t* w = (cerver_worker_t*)arg;
444
- cerver_server_t* srv = w->srv;
452
+ cerver_acceptor_t* acceptor = (cerver_acceptor_t*)arg;
453
+ cerver_server_t* srv = acceptor->srv;
445
454
 
446
455
  int kq = kqueue();
447
456
  if (kq < 0) {
448
457
  perror("cerver: kqueue");
449
458
  return NULL;
450
459
  }
451
- w->event_fd = kq;
460
+ acceptor->event_fd = kq;
452
461
 
453
462
  struct kevent change;
454
- EV_SET(&change, w->listen_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
463
+ EV_SET(&change, acceptor->listen_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
455
464
  kevent(kq, &change, 1, NULL, 0, NULL);
456
465
 
457
466
  struct kevent events[CERVER_MAX_EVENTS];
458
467
 
459
- pthread_mutex_lock(&g_acceptor_readiness.lock);
468
+ cerver_mutex_lock(&g_acceptor_readiness.lock);
460
469
  g_acceptor_readiness.acceptors_ready++;
461
- pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
470
+ cerver_cond_broadcast(&g_acceptor_readiness.ready_cv);
462
471
  while (!g_acceptor_readiness.start_accepting && srv->running)
463
- pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
464
- pthread_mutex_unlock(&g_acceptor_readiness.lock);
472
+ cerver_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
473
+ cerver_mutex_unlock(&g_acceptor_readiness.lock);
465
474
 
466
475
  while (srv->running) {
467
476
  struct timespec ts = {1, 0};
@@ -471,9 +480,9 @@ static void* acceptor_loop(void* arg) {
471
480
  break;
472
481
  }
473
482
  for (int i = 0; i < nev; i++) {
474
- if ((int)events[i].ident == w->listen_fd) {
483
+ if ((int)events[i].ident == acceptor->listen_fd) {
475
484
  while (1) {
476
- cerver_sock_t cfd = accept_connection(w->listen_fd);
485
+ cerver_sock_t cfd = accept_connection(acceptor->listen_fd);
477
486
  if (cfd == CERVER_INVALID_SOCK) {
478
487
  if (errno == EAGAIN || errno == EWOULDBLOCK) break;
479
488
  break;
@@ -497,27 +506,27 @@ static void* acceptor_loop(void* arg) {
497
506
  #elif defined(CERVER_USE_EPOLL)
498
507
 
499
508
  static void* acceptor_loop(void* arg) {
500
- cerver_worker_t* w = (cerver_worker_t*)arg;
501
- cerver_server_t* srv = w->srv;
509
+ cerver_acceptor_t* acceptor = (cerver_acceptor_t*)arg;
510
+ cerver_server_t* srv = acceptor->srv;
502
511
 
503
512
  int ep = epoll_create1(EPOLL_CLOEXEC);
504
513
  if (ep < 0) {
505
514
  perror("cerver: epoll");
506
515
  return NULL;
507
516
  }
508
- w->event_fd = ep;
517
+ acceptor->event_fd = ep;
509
518
 
510
- struct epoll_event ev = {.events = EPOLLIN, .data.fd = w->listen_fd};
511
- epoll_ctl(ep, EPOLL_CTL_ADD, w->listen_fd, &ev);
519
+ struct epoll_event ev = {.events = EPOLLIN, .data.fd = acceptor->listen_fd};
520
+ epoll_ctl(ep, EPOLL_CTL_ADD, acceptor->listen_fd, &ev);
512
521
 
513
522
  struct epoll_event events[CERVER_MAX_EVENTS];
514
523
 
515
- pthread_mutex_lock(&g_acceptor_readiness.lock);
524
+ cerver_mutex_lock(&g_acceptor_readiness.lock);
516
525
  g_acceptor_readiness.acceptors_ready++;
517
- pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
526
+ cerver_cond_broadcast(&g_acceptor_readiness.ready_cv);
518
527
  while (!g_acceptor_readiness.start_accepting && srv->running)
519
- pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
520
- pthread_mutex_unlock(&g_acceptor_readiness.lock);
528
+ cerver_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
529
+ cerver_mutex_unlock(&g_acceptor_readiness.lock);
521
530
 
522
531
  while (srv->running) {
523
532
  int nev = epoll_wait(ep, events, CERVER_MAX_EVENTS, 1000);
@@ -526,9 +535,9 @@ static void* acceptor_loop(void* arg) {
526
535
  break;
527
536
  }
528
537
  for (int i = 0; i < nev; i++) {
529
- if (events[i].data.fd == w->listen_fd) {
538
+ if (events[i].data.fd == acceptor->listen_fd) {
530
539
  while (1) {
531
- cerver_sock_t cfd = accept_connection(w->listen_fd);
540
+ cerver_sock_t cfd = accept_connection(acceptor->listen_fd);
532
541
  if (cfd < 0) {
533
542
  if (errno == EAGAIN || errno == EWOULDBLOCK) break;
534
543
  break;
@@ -549,23 +558,23 @@ static void* acceptor_loop(void* arg) {
549
558
  return NULL;
550
559
  }
551
560
 
552
- #else
561
+ #else /* SELECT — used on Windows and other POSIX platforms */
553
562
 
554
563
  static void* acceptor_loop(void* arg) {
555
- cerver_worker_t* w = (cerver_worker_t*)arg;
556
- cerver_server_t* srv = w->srv;
564
+ cerver_acceptor_t* acceptor = (cerver_acceptor_t*)arg;
565
+ cerver_server_t* srv = acceptor->srv;
557
566
 
558
- pthread_mutex_lock(&g_acceptor_readiness.lock);
567
+ cerver_mutex_lock(&g_acceptor_readiness.lock);
559
568
  g_acceptor_readiness.acceptors_ready++;
560
- pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
569
+ cerver_cond_broadcast(&g_acceptor_readiness.ready_cv);
561
570
  while (!g_acceptor_readiness.start_accepting && srv->running)
562
- pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
563
- pthread_mutex_unlock(&g_acceptor_readiness.lock);
571
+ cerver_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
572
+ cerver_mutex_unlock(&g_acceptor_readiness.lock);
564
573
 
565
574
  while (srv->running) {
566
575
  fd_set rfds;
567
576
  FD_ZERO(&rfds);
568
- FD_SET(w->listen_fd, &rfds);
577
+ FD_SET(acceptor->listen_fd, &rfds);
569
578
 
570
579
  #if CERVER_PLATFORM_WINDOWS
571
580
  /* Windows select takes (nfds, ...) but ignores the first arg for sockets */
@@ -573,7 +582,7 @@ static void* acceptor_loop(void* arg) {
573
582
  int ret = select(0, &rfds, NULL, NULL, &tv);
574
583
  #else
575
584
  struct timeval tv = {1, 0};
576
- int ret = select((int)w->listen_fd + 1, &rfds, NULL, NULL, &tv);
585
+ int ret = select((int)acceptor->listen_fd + 1, &rfds, NULL, NULL, &tv);
577
586
  #endif // CERVER_PLATFORM_WINDOWS
578
587
  if (ret < 0) {
579
588
  #if CERVER_PLATFORM_WINDOWS
@@ -583,9 +592,9 @@ static void* acceptor_loop(void* arg) {
583
592
  #endif // CERVER_PLATFORM_WINDOWS
584
593
  break;
585
594
  }
586
- if (ret > 0 && FD_ISSET(w->listen_fd, &rfds)) {
595
+ if (ret > 0 && FD_ISSET(acceptor->listen_fd, &rfds)) {
587
596
  while (1) {
588
- cerver_sock_t cfd = accept_connection(w->listen_fd);
597
+ cerver_sock_t cfd = accept_connection(acceptor->listen_fd);
589
598
  if (cfd == CERVER_INVALID_SOCK) {
590
599
  #if CERVER_PLATFORM_WINDOWS
591
600
  if (cerver_would_block_win()) break;
@@ -610,32 +619,32 @@ static void* acceptor_loop(void* arg) {
610
619
 
611
620
  void cerver_stat_cache_init(cerver_stat_cache_t* cache) {
612
621
  memset(cache, 0, sizeof(*cache));
613
- pthread_mutex_init(&cache->lock, NULL);
622
+ cerver_mutex_init(&cache->lock, NULL);
614
623
  }
615
624
 
616
625
  int cerver_stat_cache_lookup(cerver_stat_cache_t* cache, const char* path, size_t* file_size) {
617
626
  time_t now = time(NULL);
618
- pthread_mutex_lock(&cache->lock);
627
+ cerver_mutex_lock(&cache->lock);
619
628
  for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
620
629
  cerver_stat_entry_t* e = &cache->entries[i];
621
630
  if (e->valid && strcmp(e->path, path) == 0) {
622
631
  if (now - e->cached_at < CERVER_STAT_CACHE_TTL) {
623
632
  *file_size = e->file_size;
624
- pthread_mutex_unlock(&cache->lock);
633
+ cerver_mutex_unlock(&cache->lock);
625
634
  return 0;
626
635
  }
627
636
  e->valid = 0;
628
637
  break;
629
638
  }
630
639
  }
631
- pthread_mutex_unlock(&cache->lock);
640
+ cerver_mutex_unlock(&cache->lock);
632
641
  return -1;
633
642
  }
634
643
 
635
644
  void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_t file_size,
636
645
  time_t mtime) {
637
646
  time_t now = time(NULL);
638
- pthread_mutex_lock(&cache->lock);
647
+ cerver_mutex_lock(&cache->lock);
639
648
  int best = 0;
640
649
  time_t oldest = cache->entries[0].cached_at;
641
650
  for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
@@ -656,7 +665,7 @@ void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_
656
665
  slot->mtime = mtime;
657
666
  slot->cached_at = now;
658
667
  slot->valid = 1;
659
- pthread_mutex_unlock(&cache->lock);
668
+ cerver_mutex_unlock(&cache->lock);
660
669
  }
661
670
 
662
671
  /* ------------------------------------------------------------------ */
@@ -665,10 +674,10 @@ void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_
665
674
 
666
675
  int cerver_init(cerver_server_t* srv, int port, int threads) {
667
676
  memset(srv, 0, sizeof(*srv));
668
- srv->port = port;
669
- srv->sock_fd = CERVER_INVALID_SOCK;
670
- srv->running = 0;
671
- srv->worker_count = (threads > 0) ? threads : get_cpu_count();
677
+ srv->port = port;
678
+ srv->sock_fd = CERVER_INVALID_SOCK;
679
+ srv->running = 0;
680
+ srv->connection_worker_count = (threads > 0) ? threads : get_cpu_count();
672
681
  cerver_stat_cache_init(&srv->stat_cache);
673
682
  #if CERVER_PLATFORM_WINDOWS
674
683
  cerver_wsa_startup();
@@ -710,10 +719,11 @@ int cerver_listen(cerver_server_t* srv) {
710
719
  srv->running = 1;
711
720
  srv->acceptor_count = 0;
712
721
 
713
- int cpu_count = get_cpu_count();
714
- int configured_worker_count = srv->worker_count;
715
- int acceptor_count = cpu_count;
716
- if (acceptor_count > configured_worker_count) acceptor_count = configured_worker_count;
722
+ int cpu_count = get_cpu_count();
723
+ int configured_connection_worker_count = srv->connection_worker_count;
724
+ int acceptor_count = cpu_count;
725
+ if (acceptor_count > configured_connection_worker_count)
726
+ acceptor_count = configured_connection_worker_count;
717
727
  if (acceptor_count < 1) acceptor_count = 1;
718
728
 
719
729
  /* Windows select-based acceptor: limit to 1 until IOCP is wired */
@@ -721,88 +731,102 @@ int cerver_listen(cerver_server_t* srv) {
721
731
  acceptor_count = 1;
722
732
  #endif // CERVER_PLATFORM_WINDOWS
723
733
 
724
- int pool_size = configured_worker_count * 16;
725
- if (pool_size < CERVER_CONN_POOL_SIZE) pool_size = CERVER_CONN_POOL_SIZE;
726
- if (pool_size > 1024) pool_size = 1024;
734
+ int connection_worker_thread_count = configured_connection_worker_count * 16;
735
+ if (connection_worker_thread_count < CERVER_CONNECTION_WORKER_POOL_MIN)
736
+ connection_worker_thread_count = CERVER_CONNECTION_WORKER_POOL_MIN;
737
+ if (connection_worker_thread_count > 1024) connection_worker_thread_count = 1024;
727
738
 
728
739
  cq_init(&g_conn_queue);
729
740
 
730
- pthread_mutex_lock(&g_worker_readiness.lock);
731
- g_worker_readiness.workers_ready = 0;
732
- g_worker_readiness.workers_expected = pool_size;
733
- pthread_mutex_unlock(&g_worker_readiness.lock);
741
+ cerver_mutex_lock(&g_connection_worker_readiness.lock);
742
+ g_connection_worker_readiness.connection_workers_ready = 0;
743
+ g_connection_worker_readiness.connection_workers_expected = connection_worker_thread_count;
744
+ cerver_mutex_unlock(&g_connection_worker_readiness.lock);
734
745
 
735
- pthread_mutex_lock(&g_acceptor_readiness.lock);
746
+ cerver_mutex_lock(&g_acceptor_readiness.lock);
736
747
  g_acceptor_readiness.acceptors_ready = 0;
737
748
  g_acceptor_readiness.start_accepting = 0;
738
- pthread_mutex_unlock(&g_acceptor_readiness.lock);
749
+ cerver_mutex_unlock(&g_acceptor_readiness.lock);
739
750
 
740
- pthread_t* pool_threads = calloc((size_t)pool_size, sizeof(pthread_t));
741
- if (!pool_threads) {
742
- perror("cerver: calloc pool");
751
+ cerver_connection_worker_thread_t* connection_workers =
752
+ calloc((size_t)connection_worker_thread_count, sizeof(cerver_connection_worker_thread_t));
753
+ if (!connection_workers) {
754
+ perror("cerver: calloc connection workers");
743
755
  return -1;
744
756
  }
745
757
 
746
- pthread_attr_t attr;
747
- pthread_attr_init(&attr);
748
- pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
758
+ cerver_connection_worker_thread_attr_t worker_attr;
759
+ cerver_acceptor_thread_attr_t acceptor_attr;
760
+ cerver_connection_worker_thread_attr_init(&worker_attr);
761
+ cerver_acceptor_thread_attr_init(&acceptor_attr);
762
+ cerver_connection_worker_thread_attr_setstacksize(&worker_attr, 2 * 1024 * 1024);
763
+ cerver_acceptor_thread_attr_setstacksize(&acceptor_attr, 2 * 1024 * 1024);
749
764
 
750
- for (int i = 0; i < pool_size; i++) {
751
- if (pthread_create(&pool_threads[i], &attr, conn_pool_worker, srv) != 0) {
752
- perror("cerver: pool thread create");
765
+ for (int i = 0; i < connection_worker_thread_count; i++) {
766
+ if (cerver_connection_worker_thread_create(&connection_workers[i], &worker_attr,
767
+ connection_worker_loop, srv) != 0) {
768
+ perror("cerver: connection worker thread create");
753
769
  srv->running = 0;
754
- for (int j = 0; j < i; j++) pthread_join(pool_threads[j], NULL);
755
- free(pool_threads);
756
- pthread_attr_destroy(&attr);
770
+ for (int j = 0; j < i; j++) cerver_connection_worker_thread_join(connection_workers[j], NULL);
771
+ free(connection_workers);
772
+ cerver_connection_worker_thread_attr_destroy(&worker_attr);
773
+ cerver_acceptor_thread_attr_destroy(&acceptor_attr);
757
774
  return -1;
758
775
  }
759
776
  }
760
777
 
761
- if (!wait_for_pool_workers_ready(srv, pool_size)) {
778
+ if (!wait_for_connection_workers_ready(srv, connection_worker_thread_count)) {
762
779
  srv->running = 0;
763
- for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
764
- free(pool_threads);
765
- pthread_attr_destroy(&attr);
780
+ for (int i = 0; i < connection_worker_thread_count; i++)
781
+ cerver_connection_worker_thread_join(connection_workers[i], NULL);
782
+ free(connection_workers);
783
+ cerver_connection_worker_thread_attr_destroy(&worker_attr);
784
+ cerver_acceptor_thread_attr_destroy(&acceptor_attr);
766
785
  return -1;
767
786
  }
768
787
 
769
788
  srv->sock_fd = create_listener(srv->port, 1);
770
789
  if (srv->sock_fd == CERVER_INVALID_SOCK) {
771
790
  srv->running = 0;
772
- for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
773
- free(pool_threads);
774
- pthread_attr_destroy(&attr);
791
+ for (int i = 0; i < connection_worker_thread_count; i++)
792
+ cerver_connection_worker_thread_join(connection_workers[i], NULL);
793
+ free(connection_workers);
794
+ cerver_connection_worker_thread_attr_destroy(&worker_attr);
795
+ cerver_acceptor_thread_attr_destroy(&acceptor_attr);
775
796
  return -1;
776
797
  }
777
798
 
778
- srv->workers = calloc((size_t)acceptor_count, sizeof(cerver_worker_t));
799
+ srv->acceptors = calloc((size_t)acceptor_count, sizeof(cerver_acceptor_t));
779
800
  srv->acceptor_count = acceptor_count;
780
- if (!srv->workers) {
801
+ if (!srv->acceptors) {
781
802
  perror("cerver: calloc acceptors");
782
803
  srv->running = 0;
783
- pthread_cond_broadcast(&g_conn_queue.not_empty);
784
- for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
785
- free(pool_threads);
804
+ cerver_cond_broadcast(&g_conn_queue.not_empty);
805
+ for (int i = 0; i < connection_worker_thread_count; i++)
806
+ cerver_connection_worker_thread_join(connection_workers[i], NULL);
807
+ free(connection_workers);
786
808
  cerver_platform_close(srv->sock_fd);
787
- pthread_attr_destroy(&attr);
809
+ cerver_connection_worker_thread_attr_destroy(&worker_attr);
810
+ cerver_acceptor_thread_attr_destroy(&acceptor_attr);
788
811
  return -1;
789
812
  }
790
813
 
791
814
  for (int i = 0; i < acceptor_count; i++) {
792
- cerver_worker_t* w = &srv->workers[i];
793
- w->id = i;
794
- w->srv = srv;
795
- w->event_fd = -1;
815
+ cerver_acceptor_t* acceptor = &srv->acceptors[i];
816
+ acceptor->id = i;
817
+ acceptor->srv = srv;
818
+ acceptor->event_fd = -1;
796
819
  #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
797
- w->listen_fd = (i == 0) ? srv->sock_fd : create_listener(srv->port, 1);
798
- if (w->listen_fd == CERVER_INVALID_SOCK) w->listen_fd = srv->sock_fd;
820
+ acceptor->listen_fd = (i == 0) ? srv->sock_fd : create_listener(srv->port, 1);
821
+ if (acceptor->listen_fd == CERVER_INVALID_SOCK) acceptor->listen_fd = srv->sock_fd;
799
822
  #else
800
- w->listen_fd = srv->sock_fd;
823
+ acceptor->listen_fd = srv->sock_fd;
801
824
  #endif // __linux__ || __APPLE__ || __FreeBSD__
802
- if (pthread_create(&w->thread, &attr, acceptor_loop, w) != 0) {
825
+ if (cerver_acceptor_thread_create(&acceptor->thread, &acceptor_attr, acceptor_loop, acceptor) !=
826
+ 0) {
803
827
  perror("cerver: acceptor create");
804
828
  srv->running = 0;
805
- for (int j = 0; j < i; j++) pthread_join(srv->workers[j].thread, NULL);
829
+ for (int j = 0; j < i; j++) cerver_acceptor_thread_join(srv->acceptors[j].thread, NULL);
806
830
  break;
807
831
  }
808
832
  }
@@ -810,16 +834,19 @@ int cerver_listen(cerver_server_t* srv) {
810
834
  if (!wait_for_acceptors_ready(srv, acceptor_count)) {
811
835
  srv->running = 0;
812
836
  release_acceptors();
813
- for (int i = 0; i < acceptor_count; i++) pthread_join(srv->workers[i].thread, NULL);
814
- for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
815
- free(pool_threads);
816
- pthread_attr_destroy(&attr);
837
+ for (int i = 0; i < acceptor_count; i++)
838
+ cerver_acceptor_thread_join(srv->acceptors[i].thread, NULL);
839
+ for (int i = 0; i < connection_worker_thread_count; i++)
840
+ cerver_connection_worker_thread_join(connection_workers[i], NULL);
841
+ free(connection_workers);
842
+ cerver_connection_worker_thread_attr_destroy(&worker_attr);
843
+ cerver_acceptor_thread_attr_destroy(&acceptor_attr);
817
844
  return -1;
818
845
  }
819
846
 
820
847
  printf("cerver: listening on http://localhost:%d\n", srv->port);
821
848
  printf("cerver: %d acceptor(s), %d connection workers, keep-alive max %d req/conn\n",
822
- acceptor_count, pool_size, CERVER_KEEPALIVE_MAX);
849
+ acceptor_count, connection_worker_thread_count, CERVER_KEEPALIVE_MAX);
823
850
 
824
851
  for (int i = 0; i < srv->route_count; i++) {
825
852
  const char* method = srv->routes[i].method;
@@ -838,21 +865,24 @@ int cerver_listen(cerver_server_t* srv) {
838
865
  fflush(stdout);
839
866
 
840
867
  release_acceptors();
841
- pthread_attr_destroy(&attr);
868
+ cerver_connection_worker_thread_attr_destroy(&worker_attr);
869
+ cerver_acceptor_thread_attr_destroy(&acceptor_attr);
842
870
 
843
871
  /* Main thread spin — sleep(1) works on all platforms via win_compat.h macro */
844
872
  while (srv->running) sleep(1);
845
873
 
846
874
  /* Shutdown */
847
875
  srv->running = 0;
848
- for (int i = 0; i < acceptor_count; i++) pthread_join(srv->workers[i].thread, NULL);
876
+ for (int i = 0; i < acceptor_count; i++)
877
+ cerver_acceptor_thread_join(srv->acceptors[i].thread, NULL);
849
878
 
850
- pthread_mutex_lock(&g_conn_queue.lock);
851
- pthread_cond_broadcast(&g_conn_queue.not_empty);
852
- pthread_mutex_unlock(&g_conn_queue.lock);
879
+ cerver_mutex_lock(&g_conn_queue.lock);
880
+ cerver_cond_broadcast(&g_conn_queue.not_empty);
881
+ cerver_mutex_unlock(&g_conn_queue.lock);
853
882
 
854
- for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
855
- free(pool_threads);
883
+ for (int i = 0; i < connection_worker_thread_count; i++)
884
+ cerver_connection_worker_thread_join(connection_workers[i], NULL);
885
+ free(connection_workers);
856
886
 
857
887
  cerver_shutdown(srv);
858
888
  return 0;
@@ -865,22 +895,22 @@ int cerver_listen(cerver_server_t* srv) {
865
895
  void cerver_shutdown(cerver_server_t* srv) {
866
896
  srv->running = 0;
867
897
 
868
- if (srv->workers) {
898
+ if (srv->acceptors) {
869
899
  for (int i = 0; i < srv->acceptor_count; i++) {
870
900
  #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
871
- if (srv->workers[i].listen_fd != srv->sock_fd &&
872
- srv->workers[i].listen_fd != CERVER_INVALID_SOCK)
873
- cerver_platform_close(srv->workers[i].listen_fd);
901
+ if (srv->acceptors[i].listen_fd != srv->sock_fd &&
902
+ srv->acceptors[i].listen_fd != CERVER_INVALID_SOCK)
903
+ cerver_platform_close(srv->acceptors[i].listen_fd);
874
904
  #endif // __linux__ || __APPLE__ || __FreeBSD__
875
- if (srv->workers[i].event_fd >= 0)
905
+ if (srv->acceptors[i].event_fd >= 0)
876
906
  #if !CERVER_PLATFORM_WINDOWS
877
- close(srv->workers[i].event_fd);
907
+ close(srv->acceptors[i].event_fd);
878
908
  #else
879
- closesocket((SOCKET)srv->workers[i].event_fd);
909
+ closesocket((SOCKET)srv->acceptors[i].event_fd);
880
910
  #endif // !CERVER_PLATFORM_WINDOWS
881
911
  }
882
- free(srv->workers);
883
- srv->workers = NULL;
912
+ free(srv->acceptors);
913
+ srv->acceptors = NULL;
884
914
  }
885
915
 
886
916
  if (srv->route_trie) {
@@ -894,7 +924,7 @@ void cerver_shutdown(cerver_server_t* srv) {
894
924
  }
895
925
 
896
926
  cq_destroy(&g_conn_queue);
897
- pthread_mutex_destroy(&srv->stat_cache.lock);
927
+ cerver_mutex_destroy(&srv->stat_cache.lock);
898
928
 
899
929
  #if CERVER_PLATFORM_WINDOWS
900
930
  cerver_wsa_cleanup();