@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.
@@ -8,10 +8,49 @@
8
8
  #include <stdio.h>
9
9
  #include <stdlib.h>
10
10
  #include <string.h>
11
- #include <sys/mman.h>
12
11
  #include <sys/stat.h>
13
12
  #include <time.h>
13
+
14
+ #if CERVER_PLATFORM_WINDOWS
15
+ #include <direct.h>
16
+ #include <io.h>
17
+ #ifndef PATH_MAX
18
+ #define PATH_MAX _MAX_PATH
19
+ #endif // PATH_MAX
20
+ #define cerver_test_open _open
21
+ #define cerver_test_close _close
22
+ #define cerver_test_read _read
23
+ #define cerver_test_write _write
24
+ #define cerver_test_pipe(fds) _pipe((fds), 4096, _O_BINARY)
25
+ #define cerver_test_unlink _unlink
26
+ #define cerver_test_rmdir _rmdir
27
+ #define cerver_test_mkdir(path, mode) _mkdir(path)
28
+ #else
29
+ #include <sys/mman.h>
14
30
  #include <unistd.h>
31
+ #define cerver_test_open open
32
+ #define cerver_test_close close
33
+ #define cerver_test_read read
34
+ #define cerver_test_write write
35
+ #define cerver_test_pipe(fds) pipe((fds))
36
+ #define cerver_test_unlink unlink
37
+ #define cerver_test_rmdir rmdir
38
+ #define cerver_test_mkdir(path, mode) mkdir((path), (mode))
39
+ #endif // CERVER_PLATFORM_WINDOWS
40
+
41
+ #if !CERVER_PLATFORM_WINDOWS
42
+ char* mkdtemp(char* tmpl);
43
+ #endif // !CERVER_PLATFORM_WINDOWS
44
+
45
+ static char* cerver_test_mkdtemp(char* tmpl) {
46
+ #if CERVER_PLATFORM_WINDOWS
47
+ if (_mktemp(tmpl) == NULL) return NULL;
48
+ if (_mkdir(tmpl) != 0) return NULL;
49
+ return tmpl;
50
+ #else
51
+ return mkdtemp(tmpl);
52
+ #endif // CERVER_PLATFORM_WINDOWS
53
+ }
15
54
 
16
55
  static const char* res_header(const cerver_response_t* res, const char* key) {
17
56
  for (int i = 0; i < res->header_count; i++) {
@@ -29,28 +68,28 @@ static void req_add_header(cerver_request_t* req, const char* key, const char* v
29
68
  }
30
69
 
31
70
  static int write_file(const char* path, const void* data, size_t len) {
32
- int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0600);
71
+ int fd = cerver_test_open(path, O_CREAT | O_WRONLY | O_TRUNC, 0600);
33
72
  if (fd < 0) {
34
73
  return -1;
35
74
  }
36
75
  const unsigned char* p = (const unsigned char*)data;
37
76
  size_t off = 0;
38
77
  while (off < len) {
39
- ssize_t n = write(fd, p + off, len - off);
78
+ ssize_t n = cerver_test_write(fd, p + off, len - off);
40
79
  if (n <= 0) {
41
- close(fd);
80
+ cerver_test_close(fd);
42
81
  return -1;
43
82
  }
44
83
  off += (size_t)n;
45
84
  }
46
- close(fd);
85
+ cerver_test_close(fd);
47
86
  return 0;
48
87
  }
49
88
 
50
89
  static ssize_t read_all(int fd, char* buf, size_t cap) {
51
90
  size_t off = 0;
52
91
  while (off + 1 < cap) {
53
- ssize_t n = read(fd, buf + off, cap - off - 1);
92
+ ssize_t n = cerver_test_read(fd, buf + off, cap - off - 1);
54
93
  if (n < 0) {
55
94
  return -1;
56
95
  }
@@ -114,7 +153,7 @@ static void test_parse_request_trailing_slash(void) {
114
153
 
115
154
  static void test_write_response_keepalive(void) {
116
155
  int fds[2];
117
- MU_ASSERT(pipe(fds) == 0);
156
+ MU_ASSERT(cerver_test_pipe(fds) == 0);
118
157
 
119
158
  cerver_response_t res;
120
159
  memset(&res, 0, sizeof(res));
@@ -122,11 +161,11 @@ static void test_write_response_keepalive(void) {
122
161
  cerver_res_header(&res, "X-Test", "1");
123
162
 
124
163
  MU_ASSERT_EQ_INT(0, cerver_write_response(fds[1], &res, 1));
125
- close(fds[1]);
164
+ cerver_test_close(fds[1]);
126
165
 
127
166
  char out[1024];
128
167
  MU_ASSERT(read_all(fds[0], out, sizeof(out)) > 0);
129
- close(fds[0]);
168
+ cerver_test_close(fds[0]);
130
169
 
131
170
  MU_ASSERT(strstr(out, "HTTP/1.1 200 OK\r\n") != NULL);
132
171
  MU_ASSERT(strstr(out, "Content-Type: text/plain; charset=utf-8\r\n") != NULL);
@@ -139,7 +178,7 @@ static void test_write_response_keepalive(void) {
139
178
 
140
179
  static void test_write_response_force_close(void) {
141
180
  int fds[2];
142
- MU_ASSERT(pipe(fds) == 0);
181
+ MU_ASSERT(cerver_test_pipe(fds) == 0);
143
182
 
144
183
  cerver_response_t res;
145
184
  memset(&res, 0, sizeof(res));
@@ -147,11 +186,11 @@ static void test_write_response_force_close(void) {
147
186
  res._force_close = 1;
148
187
 
149
188
  MU_ASSERT_EQ_INT(0, cerver_write_response(fds[1], &res, 1));
150
- close(fds[1]);
189
+ cerver_test_close(fds[1]);
151
190
 
152
191
  char out[512];
153
192
  MU_ASSERT(read_all(fds[0], out, sizeof(out)) > 0);
154
- close(fds[0]);
193
+ cerver_test_close(fds[0]);
155
194
 
156
195
  MU_ASSERT(strstr(out, "Connection: close\r\n") != NULL);
157
196
  }
@@ -314,7 +353,7 @@ static void test_static_embedded_index_fallback(void) {
314
353
 
315
354
  static void test_static_filesystem_directory_fallback(void) {
316
355
  char dir_template[] = "/tmp/cerver-test-XXXXXX";
317
- char* dir = mkdtemp(dir_template);
356
+ char* dir = cerver_test_mkdtemp(dir_template);
318
357
  MU_ASSERT(dir != NULL);
319
358
 
320
359
  /* Create public/index.html (maps to /) */
@@ -325,7 +364,7 @@ static void test_static_filesystem_directory_fallback(void) {
325
364
  /* Create public/about/about.html (maps to /about or /about/) */
326
365
  char sub_dir[PATH_MAX];
327
366
  snprintf(sub_dir, sizeof(sub_dir), "%s/about", dir);
328
- MU_ASSERT_EQ_INT(0, mkdir(sub_dir, 0700));
367
+ MU_ASSERT_EQ_INT(0, cerver_test_mkdir(sub_dir, 0700));
329
368
 
330
369
  char file_path2[PATH_MAX];
331
370
  snprintf(file_path2, sizeof(file_path2), "%s/about/about.html", dir);
@@ -352,7 +391,7 @@ static void test_static_filesystem_directory_fallback(void) {
352
391
 
353
392
  MU_ASSERT_EQ_INT(0, cerver_serve_static(&srv, &req, &res));
354
393
  MU_ASSERT_EQ_SIZE(10, res.body_len);
355
- if (res._body_owned == 3 && res._file_fd >= 0) close(res._file_fd);
394
+ if (res._body_owned == 3 && res._file_fd >= 0) cerver_test_close(res._file_fd);
356
395
  }
357
396
 
358
397
  /* Test /about -> about/about.html fallback */
@@ -366,7 +405,7 @@ static void test_static_filesystem_directory_fallback(void) {
366
405
 
367
406
  MU_ASSERT_EQ_INT(0, cerver_serve_static(&srv, &req, &res));
368
407
  MU_ASSERT_EQ_SIZE(13, res.body_len);
369
- if (res._body_owned == 3 && res._file_fd >= 0) close(res._file_fd);
408
+ if (res._body_owned == 3 && res._file_fd >= 0) cerver_test_close(res._file_fd);
370
409
  }
371
410
 
372
411
  /* Test /about/ -> about/about.html fallback */
@@ -380,19 +419,19 @@ static void test_static_filesystem_directory_fallback(void) {
380
419
 
381
420
  MU_ASSERT_EQ_INT(0, cerver_serve_static(&srv, &req, &res));
382
421
  MU_ASSERT_EQ_SIZE(13, res.body_len);
383
- if (res._body_owned == 3 && res._file_fd >= 0) close(res._file_fd);
422
+ if (res._body_owned == 3 && res._file_fd >= 0) cerver_test_close(res._file_fd);
384
423
  }
385
424
 
386
- unlink(file_path);
387
- unlink(file_path2);
388
- unlink(file_path3);
389
- rmdir(sub_dir);
390
- rmdir(dir);
425
+ cerver_test_unlink(file_path);
426
+ cerver_test_unlink(file_path2);
427
+ cerver_test_unlink(file_path3);
428
+ cerver_test_rmdir(sub_dir);
429
+ cerver_test_rmdir(dir);
391
430
  }
392
431
 
393
432
  static void test_static_filesystem_small(void) {
394
433
  char dir_template[] = "/tmp/cerver-test-XXXXXX";
395
- char* dir = mkdtemp(dir_template);
434
+ char* dir = cerver_test_mkdtemp(dir_template);
396
435
  MU_ASSERT(dir != NULL);
397
436
 
398
437
  char file_path[PATH_MAX];
@@ -417,29 +456,29 @@ static void test_static_filesystem_small(void) {
417
456
  MU_ASSERT(res._file_fd >= 0);
418
457
 
419
458
  int fds[2];
420
- MU_ASSERT(pipe(fds) == 0);
459
+ MU_ASSERT(cerver_test_pipe(fds) == 0);
421
460
  MU_ASSERT_EQ_INT(0, cerver_write_response(fds[1], &res, 1));
422
- close(fds[1]);
461
+ cerver_test_close(fds[1]);
423
462
 
424
463
  char out[1024];
425
464
  ssize_t n = read_all(fds[0], out, sizeof(out));
426
465
  MU_ASSERT(n > 0);
427
- close(fds[0]);
466
+ cerver_test_close(fds[0]);
428
467
 
429
468
  MU_ASSERT(strstr(out, "HTTP/1.1 200 OK\r\n") != NULL);
430
469
  MU_ASSERT(strstr(out, "Content-Length: 5\r\n") != NULL);
431
470
  MU_ASSERT(strstr(out, "\r\nsmall") != NULL);
432
471
 
433
472
  if (res._body_owned == 3 && res._file_fd >= 0) {
434
- close(res._file_fd);
473
+ cerver_test_close(res._file_fd);
435
474
  }
436
- unlink(file_path);
437
- rmdir(dir);
475
+ cerver_test_unlink(file_path);
476
+ cerver_test_rmdir(dir);
438
477
  }
439
478
 
440
479
  static void test_static_filesystem_large(void) {
441
480
  char dir_template[] = "/tmp/cerver-test-XXXXXX";
442
- char* dir = mkdtemp(dir_template);
481
+ char* dir = cerver_test_mkdtemp(dir_template);
443
482
  MU_ASSERT(dir != NULL);
444
483
 
445
484
  char file_path[PATH_MAX];
@@ -469,23 +508,23 @@ static void test_static_filesystem_large(void) {
469
508
  MU_ASSERT(res._file_fd >= 0);
470
509
 
471
510
  int fds[2];
472
- MU_ASSERT(pipe(fds) == 0);
511
+ MU_ASSERT(cerver_test_pipe(fds) == 0);
473
512
  MU_ASSERT_EQ_INT(0, cerver_write_response(fds[1], &res, 1));
474
- close(fds[1]);
513
+ cerver_test_close(fds[1]);
475
514
 
476
515
  char out[35000];
477
516
  ssize_t n = read_all(fds[0], out, sizeof(out));
478
517
  MU_ASSERT(n > 0);
479
- close(fds[0]);
518
+ cerver_test_close(fds[0]);
480
519
 
481
520
  MU_ASSERT(strstr(out, "HTTP/1.1 200 OK\r\n") != NULL);
482
521
  MU_ASSERT(strstr(out, "Content-Length: 32000\r\n") != NULL);
483
522
 
484
523
  if (res._body_owned == 3 && res._file_fd >= 0) {
485
- close(res._file_fd);
524
+ cerver_test_close(res._file_fd);
486
525
  }
487
- unlink(file_path);
488
- rmdir(dir);
526
+ cerver_test_unlink(file_path);
527
+ cerver_test_rmdir(dir);
489
528
  }
490
529
 
491
530
  static void test_static_rejects_unsafe_path(void) {
@@ -517,8 +556,8 @@ static void test_cerver_init_fields(void) {
517
556
  cerver_server_t srv;
518
557
  cerver_init(&srv, 9090, 3);
519
558
  MU_ASSERT_EQ_INT(9090, srv.port);
520
- MU_ASSERT_EQ_INT(3, srv.worker_count);
521
- MU_ASSERT_EQ_INT(-1, srv.sock_fd);
559
+ MU_ASSERT_EQ_INT(3, srv.connection_worker_count);
560
+ MU_ASSERT(srv.sock_fd == CERVER_INVALID_SOCK);
522
561
  MU_ASSERT_EQ_INT(0, srv.running);
523
562
  }
524
563
 
@@ -2,7 +2,8 @@
2
2
  * win_compat.h — Windows (Winsock2 / MSVC / MinGW) compatibility shim.
3
3
  *
4
4
  * Include BEFORE any system headers in files that use sockets, threads,
5
- * or POSIX I/O. On non-Windows platforms this header is a no-op.
5
+ * or POSIX I/O. On non-Windows platforms this exposes POSIX-backed
6
+ * runtime aliases.
6
7
  */
7
8
 
8
9
  #ifndef CERVER_WIN_COMPAT_H
@@ -21,16 +22,17 @@
21
22
  #include <winsock2.h>
22
23
  #include <ws2tcpip.h>
23
24
  #include <windows.h>
25
+ #include <stddef.h>
26
+ #include <stdint.h>
27
+ #include <stdlib.h>
28
+ #include <string.h>
24
29
  #include <io.h>
30
+ #include <fcntl.h>
25
31
  #include <process.h>
26
32
  #include <time.h>
27
33
 
28
- /* ---- pthread shim via Windows threads --------------------------- */
29
- /* We bundle a minimal pthreads-win32 surface so the rest of the code
30
- can stay unchanged. Only the primitives actually used by cerver are
31
- mapped; this is NOT a general replacement. */
32
-
33
- #include <pthread.h>
34
+ /* ---- native thread primitives ------------------------------------ */
35
+ /* Only the primitives actually used by cerver are exposed here. */
34
36
 
35
37
  /* ---- POSIX socket aliases --------------------------------------- */
36
38
  typedef SOCKET cerver_sock_t;
@@ -77,17 +79,23 @@ static inline void* cerver_memmem_win(const void* hay, size_t hlen, const void*
77
79
  #define memmem cerver_memmem_win
78
80
  #endif // memmem
79
81
 
80
- /* clock_gettime is missing before MSVC 2019 / older MinGW */
81
- #if defined(_MSC_VER) && _MSC_VER < 1900
82
- #include <windows.h>
82
+ /* POSIX case-insensitive string helpers are named differently by the Windows CRT. */
83
+ #ifndef strcasecmp
84
+ #define strcasecmp _stricmp
85
+ #endif // strcasecmp
86
+
87
+ /* clock_gettime / CLOCK_REALTIME are missing from some Windows CRTs. */
83
88
  #ifndef CLOCK_REALTIME
84
89
  #define CLOCK_REALTIME 0
85
- typedef struct {
90
+ typedef struct cerver_timespec_t {
86
91
  long tv_sec;
87
92
  long tv_nsec;
88
- } timespec_t;
89
- #define timespec timespec_t
90
- static inline int clock_gettime(int, struct timespec* ts) {
93
+ } cerver_timespec_t;
94
+ #ifndef timespec
95
+ #define timespec cerver_timespec_t
96
+ #endif // timespec
97
+ static inline int clock_gettime(int clk_id, cerver_timespec_t* ts) {
98
+ (void)clk_id;
91
99
  FILETIME ft;
92
100
  GetSystemTimeAsFileTime(&ft);
93
101
  ULONGLONG t = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
@@ -97,7 +105,6 @@ static inline int clock_gettime(int, struct timespec* ts) {
97
105
  return 0;
98
106
  }
99
107
  #endif // CLOCK_REALTIME
100
- #endif // _MSC_VER && _MSC_VER < 1900
101
108
 
102
109
  /* Windows socket error → errno translation for the few codes we check */
103
110
  static inline int cerver_would_block_win(void) {
@@ -124,6 +131,170 @@ static inline long cerver_nproc_win(void) {
124
131
  #endif // SIGPIPE
125
132
  /* SIG_IGN is defined in <signal.h> on Windows */
126
133
 
134
+ /* ---- native Windows thread backend ------------------------------- */
135
+ typedef SRWLOCK cerver_mutex_t;
136
+ typedef CONDITION_VARIABLE cerver_cond_t;
137
+ typedef INIT_ONCE cerver_fetch_global_init_guard_t;
138
+ typedef HANDLE cerver_connection_worker_thread_t;
139
+ typedef HANDLE cerver_acceptor_thread_t;
140
+ typedef size_t cerver_connection_worker_thread_attr_t;
141
+ typedef size_t cerver_acceptor_thread_attr_t;
142
+
143
+ #define CERVER_MUTEX_INITIALIZER SRWLOCK_INIT
144
+ #define CERVER_COND_INITIALIZER CONDITION_VARIABLE_INIT
145
+ #define CERVER_FETCH_GLOBAL_INIT_GUARD_INITIALIZER INIT_ONCE_STATIC_INIT
146
+
147
+ static inline int cerver_mutex_init(cerver_mutex_t* m, void* attr) {
148
+ (void)attr;
149
+ InitializeSRWLock(m);
150
+ return 0;
151
+ }
152
+ static inline int cerver_mutex_destroy(cerver_mutex_t* m) {
153
+ (void)m;
154
+ return 0;
155
+ }
156
+ static inline int cerver_mutex_lock(cerver_mutex_t* m) {
157
+ AcquireSRWLockExclusive(m);
158
+ return 0;
159
+ }
160
+ static inline int cerver_mutex_unlock(cerver_mutex_t* m) {
161
+ ReleaseSRWLockExclusive(m);
162
+ return 0;
163
+ }
164
+
165
+ static inline int cerver_cond_init(cerver_cond_t* c, void* attr) {
166
+ (void)attr;
167
+ InitializeConditionVariable(c);
168
+ return 0;
169
+ }
170
+ static inline int cerver_cond_destroy(cerver_cond_t* c) {
171
+ (void)c;
172
+ return 0;
173
+ }
174
+ static inline int cerver_cond_wait(cerver_cond_t* c, cerver_mutex_t* m) {
175
+ SleepConditionVariableSRW(c, m, INFINITE, 0);
176
+ return 0;
177
+ }
178
+ static inline int cerver_cond_timedwait(cerver_cond_t* c, cerver_mutex_t* m,
179
+ const struct timespec* ts) {
180
+ FILETIME ft;
181
+ GetSystemTimeAsFileTime(&ft);
182
+ ULONGLONG t = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
183
+ t -= 116444736000000000ULL;
184
+ long long now_sec = (long long)(t / 10000000ULL);
185
+ long long now_nsec = (long long)((t % 10000000ULL) * 100);
186
+
187
+ long long ms = (ts->tv_sec - now_sec) * 1000 + (ts->tv_nsec - now_nsec) / 1000000;
188
+ if (ms < 0) ms = 0;
189
+ if (SleepConditionVariableSRW(c, m, (DWORD)ms, 0)) return 0;
190
+ return 110; /* ETIMEDOUT */
191
+ }
192
+ static inline int cerver_cond_signal(cerver_cond_t* c) {
193
+ WakeConditionVariable(c);
194
+ return 0;
195
+ }
196
+ static inline int cerver_cond_broadcast(cerver_cond_t* c) {
197
+ WakeAllConditionVariable(c);
198
+ return 0;
199
+ }
200
+
201
+ static inline int cerver_connection_worker_thread_attr_init(
202
+ cerver_connection_worker_thread_attr_t* attr) {
203
+ *attr = 0;
204
+ return 0;
205
+ }
206
+ static inline int cerver_connection_worker_thread_attr_destroy(
207
+ cerver_connection_worker_thread_attr_t* attr) {
208
+ *attr = 0;
209
+ return 0;
210
+ }
211
+ static inline int cerver_connection_worker_thread_attr_setstacksize(
212
+ cerver_connection_worker_thread_attr_t* attr, size_t stacksize) {
213
+ *attr = stacksize;
214
+ return 0;
215
+ }
216
+
217
+ static inline int cerver_acceptor_thread_attr_init(cerver_acceptor_thread_attr_t* attr) {
218
+ *attr = 0;
219
+ return 0;
220
+ }
221
+ static inline int cerver_acceptor_thread_attr_destroy(cerver_acceptor_thread_attr_t* attr) {
222
+ *attr = 0;
223
+ return 0;
224
+ }
225
+ static inline int cerver_acceptor_thread_attr_setstacksize(cerver_acceptor_thread_attr_t* attr,
226
+ size_t stacksize) {
227
+ *attr = stacksize;
228
+ return 0;
229
+ }
230
+
231
+ struct cerver_windows_thread_entry_args {
232
+ void* (*func)(void*);
233
+ void* arg;
234
+ };
235
+ static inline DWORD WINAPI cerver_windows_thread_entry(LPVOID lpParam) {
236
+ struct cerver_windows_thread_entry_args* args = (struct cerver_windows_thread_entry_args*)lpParam;
237
+ void* (*func)(void*) = args->func;
238
+ void* arg = args->arg;
239
+ free(args);
240
+ func(arg);
241
+ return 0;
242
+ }
243
+
244
+ static inline int cerver_start_windows_native_thread(HANDLE* thread, size_t stack,
245
+ void* (*start_routine)(void*), void* arg) {
246
+ struct cerver_windows_thread_entry_args* args =
247
+ (struct cerver_windows_thread_entry_args*)malloc(sizeof(*args));
248
+ if (!args) return -1;
249
+ args->func = start_routine;
250
+ args->arg = arg;
251
+ *thread = CreateThread(NULL, stack, cerver_windows_thread_entry, args, 0, NULL);
252
+ if (!*thread) {
253
+ free(args);
254
+ return -1;
255
+ }
256
+ return 0;
257
+ }
258
+
259
+ static inline int cerver_connection_worker_thread_create(
260
+ cerver_connection_worker_thread_t* thread, const cerver_connection_worker_thread_attr_t* attr,
261
+ void* (*start_routine)(void*), void* arg) {
262
+ return cerver_start_windows_native_thread(thread, attr ? *attr : 0, start_routine, arg);
263
+ }
264
+ static inline int cerver_acceptor_thread_create(cerver_acceptor_thread_t* thread,
265
+ const cerver_acceptor_thread_attr_t* attr,
266
+ void* (*start_routine)(void*), void* arg) {
267
+ return cerver_start_windows_native_thread(thread, attr ? *attr : 0, start_routine, arg);
268
+ }
269
+
270
+ static inline int cerver_connection_worker_thread_join(cerver_connection_worker_thread_t thread,
271
+ void** retval) {
272
+ (void)retval;
273
+ WaitForSingleObject(thread, INFINITE);
274
+ CloseHandle(thread);
275
+ return 0;
276
+ }
277
+ static inline int cerver_acceptor_thread_join(cerver_acceptor_thread_t thread, void** retval) {
278
+ (void)retval;
279
+ WaitForSingleObject(thread, INFINITE);
280
+ CloseHandle(thread);
281
+ return 0;
282
+ }
283
+
284
+ static inline BOOL CALLBACK cerver_fetch_global_init_guard_stub(PINIT_ONCE InitOnce,
285
+ PVOID Parameter, PVOID* Context) {
286
+ (void)InitOnce;
287
+ (void)Context;
288
+ void (*func)(void) = (void (*)(void))Parameter;
289
+ func();
290
+ return TRUE;
291
+ }
292
+ static inline int cerver_fetch_global_init_guard_run(cerver_fetch_global_init_guard_t* guard,
293
+ void (*init_routine)(void)) {
294
+ InitOnceExecuteOnce(guard, cerver_fetch_global_init_guard_stub, init_routine, NULL);
295
+ return 0;
296
+ }
297
+
127
298
  /* Winsock init/teardown helpers ----------------------------------- */
128
299
  static inline int cerver_wsa_startup(void) {
129
300
  WSADATA wd;
@@ -136,6 +307,8 @@ static inline void cerver_wsa_cleanup(void) { WSACleanup(); }
136
307
 
137
308
  #else /* !Windows */
138
309
 
310
+ #include <errno.h>
311
+ #include <pthread.h>
139
312
  #include <stddef.h>
140
313
  #include <sys/types.h>
141
314
  #include <unistd.h>
@@ -153,5 +326,86 @@ static inline ssize_t cerver_sock_write(int fd, const void* buf, size_t n) {
153
326
  static inline int cerver_wsa_startup(void) { return 0; }
154
327
  static inline void cerver_wsa_cleanup(void) {}
155
328
 
329
+ typedef pthread_mutex_t cerver_mutex_t;
330
+ typedef pthread_cond_t cerver_cond_t;
331
+ typedef pthread_once_t cerver_fetch_global_init_guard_t;
332
+ typedef pthread_t cerver_connection_worker_thread_t;
333
+ typedef pthread_t cerver_acceptor_thread_t;
334
+ typedef pthread_attr_t cerver_connection_worker_thread_attr_t;
335
+ typedef pthread_attr_t cerver_acceptor_thread_attr_t;
336
+
337
+ #define CERVER_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
338
+ #define CERVER_COND_INITIALIZER PTHREAD_COND_INITIALIZER
339
+ #define CERVER_FETCH_GLOBAL_INIT_GUARD_INITIALIZER PTHREAD_ONCE_INIT
340
+
341
+ static inline int cerver_mutex_init(cerver_mutex_t* m, void* attr) {
342
+ return pthread_mutex_init(m, (const pthread_mutexattr_t*)attr);
343
+ }
344
+ static inline int cerver_mutex_destroy(cerver_mutex_t* m) { return pthread_mutex_destroy(m); }
345
+ static inline int cerver_mutex_lock(cerver_mutex_t* m) { return pthread_mutex_lock(m); }
346
+ static inline int cerver_mutex_unlock(cerver_mutex_t* m) { return pthread_mutex_unlock(m); }
347
+
348
+ static inline int cerver_cond_init(cerver_cond_t* c, void* attr) {
349
+ return pthread_cond_init(c, (const pthread_condattr_t*)attr);
350
+ }
351
+ static inline int cerver_cond_destroy(cerver_cond_t* c) { return pthread_cond_destroy(c); }
352
+ static inline int cerver_cond_wait(cerver_cond_t* c, cerver_mutex_t* m) {
353
+ return pthread_cond_wait(c, m);
354
+ }
355
+ static inline int cerver_cond_timedwait(cerver_cond_t* c, cerver_mutex_t* m,
356
+ const struct timespec* ts) {
357
+ return pthread_cond_timedwait(c, m, ts);
358
+ }
359
+ static inline int cerver_cond_signal(cerver_cond_t* c) { return pthread_cond_signal(c); }
360
+ static inline int cerver_cond_broadcast(cerver_cond_t* c) { return pthread_cond_broadcast(c); }
361
+
362
+ static inline int cerver_connection_worker_thread_attr_init(
363
+ cerver_connection_worker_thread_attr_t* attr) {
364
+ return pthread_attr_init(attr);
365
+ }
366
+ static inline int cerver_connection_worker_thread_attr_destroy(
367
+ cerver_connection_worker_thread_attr_t* attr) {
368
+ return pthread_attr_destroy(attr);
369
+ }
370
+ static inline int cerver_connection_worker_thread_attr_setstacksize(
371
+ cerver_connection_worker_thread_attr_t* attr, size_t stacksize) {
372
+ return pthread_attr_setstacksize(attr, stacksize);
373
+ }
374
+
375
+ static inline int cerver_acceptor_thread_attr_init(cerver_acceptor_thread_attr_t* attr) {
376
+ return pthread_attr_init(attr);
377
+ }
378
+ static inline int cerver_acceptor_thread_attr_destroy(cerver_acceptor_thread_attr_t* attr) {
379
+ return pthread_attr_destroy(attr);
380
+ }
381
+ static inline int cerver_acceptor_thread_attr_setstacksize(cerver_acceptor_thread_attr_t* attr,
382
+ size_t stacksize) {
383
+ return pthread_attr_setstacksize(attr, stacksize);
384
+ }
385
+
386
+ static inline int cerver_connection_worker_thread_create(
387
+ cerver_connection_worker_thread_t* thread, const cerver_connection_worker_thread_attr_t* attr,
388
+ void* (*start_routine)(void*), void* arg) {
389
+ return pthread_create(thread, attr, start_routine, arg);
390
+ }
391
+ static inline int cerver_acceptor_thread_create(cerver_acceptor_thread_t* thread,
392
+ const cerver_acceptor_thread_attr_t* attr,
393
+ void* (*start_routine)(void*), void* arg) {
394
+ return pthread_create(thread, attr, start_routine, arg);
395
+ }
396
+
397
+ static inline int cerver_connection_worker_thread_join(cerver_connection_worker_thread_t thread,
398
+ void** retval) {
399
+ return pthread_join(thread, retval);
400
+ }
401
+ static inline int cerver_acceptor_thread_join(cerver_acceptor_thread_t thread, void** retval) {
402
+ return pthread_join(thread, retval);
403
+ }
404
+
405
+ static inline int cerver_fetch_global_init_guard_run(cerver_fetch_global_init_guard_t* guard,
406
+ void (*init_routine)(void)) {
407
+ return pthread_once(guard, init_routine);
408
+ }
409
+
156
410
  #endif // _WIN32 || _WIN64
157
411
  #endif // CERVER_WIN_COMPAT_H