@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/Makefile +21 -2
- package/WINDOWS.md +6 -12
- package/lib/commands/build.js +1 -1
- package/lib/compiler/compile.js +2 -41
- package/package.json +1 -1
- package/runtime/cerver.h +16 -17
- package/runtime/fetch.c +22 -5
- package/runtime/http_parser.c +4 -0
- package/runtime/http_writer.c +2 -0
- package/runtime/mime.c +4 -0
- package/runtime/router.c +3 -0
- package/runtime/server.c +207 -177
- package/runtime/tests/runtime_tests.c +77 -38
- package/runtime/win_compat.h +269 -15
|
@@ -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 =
|
|
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 =
|
|
78
|
+
ssize_t n = cerver_test_write(fd, p + off, len - off);
|
|
40
79
|
if (n <= 0) {
|
|
41
|
-
|
|
80
|
+
cerver_test_close(fd);
|
|
42
81
|
return -1;
|
|
43
82
|
}
|
|
44
83
|
off += (size_t)n;
|
|
45
84
|
}
|
|
46
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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)
|
|
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)
|
|
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)
|
|
422
|
+
if (res._body_owned == 3 && res._file_fd >= 0) cerver_test_close(res._file_fd);
|
|
384
423
|
}
|
|
385
424
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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 =
|
|
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(
|
|
459
|
+
MU_ASSERT(cerver_test_pipe(fds) == 0);
|
|
421
460
|
MU_ASSERT_EQ_INT(0, cerver_write_response(fds[1], &res, 1));
|
|
422
|
-
|
|
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
|
-
|
|
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
|
-
|
|
473
|
+
cerver_test_close(res._file_fd);
|
|
435
474
|
}
|
|
436
|
-
|
|
437
|
-
|
|
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 =
|
|
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(
|
|
511
|
+
MU_ASSERT(cerver_test_pipe(fds) == 0);
|
|
473
512
|
MU_ASSERT_EQ_INT(0, cerver_write_response(fds[1], &res, 1));
|
|
474
|
-
|
|
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
|
-
|
|
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
|
-
|
|
524
|
+
cerver_test_close(res._file_fd);
|
|
486
525
|
}
|
|
487
|
-
|
|
488
|
-
|
|
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.
|
|
521
|
-
|
|
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
|
|
package/runtime/win_compat.h
CHANGED
|
@@ -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.
|
|
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
|
-
/* ----
|
|
29
|
-
/*
|
|
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
|
-
/*
|
|
81
|
-
#
|
|
82
|
-
#
|
|
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
|
-
}
|
|
89
|
-
#
|
|
90
|
-
|
|
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
|