iodine 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -1,194 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2016-2017
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #ifndef _GNU_SOURCE
8
- #define _GNU_SOURCE
9
- #endif
10
-
11
- #include "evio.h"
12
-
13
- #ifdef EVIO_ENGINE_KQUEUE
14
-
15
- #include <sys/types.h>
16
-
17
- #include <assert.h>
18
- #include <errno.h>
19
- #include <fcntl.h>
20
- #include <netdb.h>
21
- #include <stdint.h>
22
- #include <stdio.h>
23
- #include <stdlib.h>
24
- #include <string.h>
25
- #include <sys/socket.h>
26
- #include <time.h>
27
- #include <unistd.h>
28
-
29
- #include <sys/event.h>
30
- #include <sys/time.h>
31
- /* *****************************************************************************
32
- Global data and system independant code
33
- ***************************************************************************** */
34
-
35
- static int evio_fd = -1;
36
- static pid_t owner_pid = 0;
37
-
38
- /** Closes the `epoll` / `kqueue` object, releasing it's resources. */
39
- void evio_close() {
40
- /* the file descriptor is never inherited by fork */
41
- if (evio_fd != -1 && owner_pid == getpid()) {
42
- close(evio_fd);
43
- evio_fd = -1;
44
- }
45
- }
46
-
47
- /**
48
- returns true if the evio is available for adding or removing file descriptors.
49
- */
50
- int evio_isactive(void) { return evio_fd >= 0; }
51
-
52
- /* *****************************************************************************
53
- BSD `kqueue` implementation
54
- ***************************************************************************** */
55
-
56
- /**
57
- Creates the `epoll` or `kqueue` object.
58
- */
59
- intptr_t evio_create() {
60
- evio_close();
61
- owner_pid = getpid();
62
- return evio_fd = kqueue();
63
- }
64
-
65
- /**
66
- Removes a file descriptor from the polling object.
67
- */
68
- void evio_remove(int fd) {
69
- if (evio_fd < 0)
70
- return;
71
- struct kevent chevent[3];
72
- EV_SET(chevent, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
73
- EV_SET(chevent + 1, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
74
- EV_SET(chevent + 2, fd, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
75
- kevent(evio_fd, chevent, 3, NULL, 0, NULL);
76
- }
77
-
78
- /**
79
- Adds a file descriptor to the polling object.
80
- */
81
- int evio_add(int fd, void *callback_arg) {
82
- struct kevent chevent[2];
83
- EV_SET(chevent, fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT,
84
- 0, 0, callback_arg);
85
- EV_SET(chevent + 1, fd, EVFILT_WRITE,
86
- EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, callback_arg);
87
- return kevent(evio_fd, chevent, 2, NULL, 0, NULL);
88
- }
89
-
90
- /**
91
- Adds a file descriptor to the polling object (ONE SHOT), to be polled for
92
- incoming data (`evio_on_data` wil be called).
93
- */
94
- int evio_add_read(int fd, void *callback_arg) {
95
- struct kevent chevent[1];
96
- EV_SET(chevent, fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT,
97
- 0, 0, callback_arg);
98
- return kevent(evio_fd, chevent, 1, NULL, 0, NULL);
99
- }
100
-
101
- /**
102
- Adds a file descriptor to the polling object (ONE SHOT), to be polled for
103
- outgoing buffer readiness data (`evio_on_ready` wil be called).
104
- */
105
- int evio_add_write(int fd, void *callback_arg) {
106
- struct kevent chevent[1];
107
- EV_SET(chevent, fd, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT,
108
- 0, 0, callback_arg);
109
- return kevent(evio_fd, chevent, 1, NULL, 0, NULL);
110
- }
111
-
112
- /**
113
- Creates a timer file descriptor, system dependent.
114
- */
115
- int evio_open_timer() {
116
- #ifdef P_tmpdir
117
- if (P_tmpdir[sizeof(P_tmpdir) - 1] == '/') {
118
- char name_template[] = P_tmpdir "evio_facil_timer_XXXXXX";
119
- return mkstemp(name_template);
120
- }
121
- char name_template[] = P_tmpdir "/evio_facil_timer_XXXXXX";
122
- return mkstemp(name_template);
123
- #else
124
- char name_template[] = "/tmp/evio_facil_timer_XXXXXX";
125
- return mkstemp(name_template);
126
- #endif
127
- }
128
-
129
- /**
130
- Adds a timer file descriptor, so that callbacks will be called for it's events.
131
- */
132
- int evio_set_timer(int fd, void *callback_arg, unsigned long milliseconds) {
133
- struct kevent chevent;
134
- EV_SET(&chevent, fd, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0,
135
- milliseconds, callback_arg);
136
- return kevent(evio_fd, &chevent, 1, NULL, 0, NULL);
137
- }
138
-
139
- /**
140
- Reviews any pending events (up to EVIO_MAX_EVENTS) and calls any callbacks.
141
- */
142
- int evio_review(const int timeout_millisec) {
143
- if (evio_fd < 0)
144
- return -1;
145
- struct kevent events[EVIO_MAX_EVENTS];
146
-
147
- const struct timespec timeout = {.tv_sec = (timeout_millisec / 1024),
148
- .tv_nsec =
149
- ((timeout_millisec % 1024) * 1000000)};
150
- /* wait for events and handle them */
151
- int active_count =
152
- kevent(evio_fd, NULL, 0, events, EVIO_MAX_EVENTS, &timeout);
153
-
154
- if (active_count > 0) {
155
- for (int i = 0; i < active_count; i++) {
156
- // test for event(s) type
157
- if (events[i].filter == EVFILT_READ || events[i].filter == EVFILT_TIMER) {
158
- evio_on_data(events[i].udata);
159
- }
160
- // connection errors should be reported after `read` in case there's data
161
- // left in the buffer.
162
- if (events[i].flags & (EV_EOF | EV_ERROR)) {
163
- // errors are hendled as disconnections (on_close)
164
- // fprintf(stderr, "%p: %s\n", events[i].udata,
165
- // (events[i].flags & EV_EOF)
166
- // ? "EV_EOF"
167
- // : (events[i].flags & EV_ERROR) ? "EV_ERROR" : "WTF?");
168
- evio_on_error(events[i].udata);
169
- } else if (events[i].filter == EVFILT_WRITE) {
170
- // we can only write if there's no error in the socket
171
- evio_on_ready(events[i].udata);
172
- }
173
- }
174
- } else if (active_count < 0) {
175
- if (errno == EINTR)
176
- return 0;
177
- return -1;
178
- }
179
- return active_count;
180
- }
181
-
182
- #include <poll.h>
183
-
184
- /** Waits up to `timeout_millisec` for events. No events are signaled. */
185
- int evio_wait(const int timeout_millisec) {
186
- if (evio_fd < 0)
187
- return -1;
188
- struct pollfd pollfd = {
189
- .fd = evio_fd, .events = POLLIN,
190
- };
191
- return poll(&pollfd, 1, timeout_millisec);
192
- }
193
-
194
- #endif /* system dependent code */
@@ -1,2325 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2016-2017
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #include "spnlock.inc"
8
-
9
- #include "evio.h"
10
- #include "facil.h"
11
- #include "fio_hashmap.h"
12
- #include "fiobj4sock.h"
13
-
14
- #include "fio_mem.h"
15
-
16
- #include <errno.h>
17
- #include <pthread.h>
18
- #include <signal.h>
19
- #include <stdio.h>
20
- #include <stdlib.h>
21
- #include <string.h>
22
- #include <sys/mman.h>
23
- #include <sys/stat.h>
24
- #include <sys/wait.h>
25
-
26
- #if !defined(__GNUC__) && !defined(__clang__)
27
- #define __attribute__(...)
28
- #endif
29
-
30
- /* *****************************************************************************
31
- Patch for OSX version < 10.12 from https://stackoverflow.com/a/9781275/4025095
32
- ***************************************************************************** */
33
- #if defined(__MACH__) && !defined(CLOCK_REALTIME)
34
- #include <sys/time.h>
35
- #define CLOCK_REALTIME 0
36
- #define clock_gettime patch_clock_gettime
37
- // clock_gettime is not implemented on older versions of OS X (< 10.12).
38
- // If implemented, CLOCK_REALTIME will have already been defined.
39
- static inline int patch_clock_gettime(int clk_id, struct timespec *t) {
40
- struct timeval now;
41
- int rv = gettimeofday(&now, NULL);
42
- if (rv)
43
- return rv;
44
- t->tv_sec = now.tv_sec;
45
- t->tv_nsec = now.tv_usec * 1000;
46
- return 0;
47
- (void)clk_id;
48
- }
49
- #endif
50
-
51
- /* *****************************************************************************
52
- Data Structures
53
- ***************************************************************************** */
54
- typedef struct ProtocolMetadata {
55
- spn_lock_i locks[3];
56
- unsigned rsv : 8;
57
- } protocol_metadata_s;
58
-
59
- union protocol_metadata_union_u {
60
- size_t opaque;
61
- protocol_metadata_s meta;
62
- };
63
- #define prt_meta(prt) (((union protocol_metadata_union_u *)(&(prt)->rsv))->meta)
64
-
65
- struct connection_data_s {
66
- protocol_s *protocol;
67
- time_t active;
68
- uint8_t timeout;
69
- spn_lock_i scheduled;
70
- spn_lock_i lock;
71
- };
72
-
73
- static struct facil_data_s {
74
- spn_lock_i global_lock;
75
- uint8_t need_review;
76
- uint8_t spindown;
77
- uint16_t active;
78
- uint16_t threads;
79
- pid_t parent;
80
- pool_pt thread_pool;
81
- ssize_t capacity;
82
- size_t connection_count;
83
- void (*on_idle)(void);
84
- void (*on_finish)(void);
85
- struct timespec last_cycle;
86
- struct connection_data_s conn[];
87
- } * facil_data;
88
-
89
- #define fd_data(fd) (facil_data->conn[(fd)])
90
- #define uuid_data(uuid) fd_data(sock_uuid2fd((uuid)))
91
- // #define uuid_prt_meta(uuid) prt_meta(uuid_data((uuid)).protocol)
92
-
93
- /** locks a connection's protocol returns a pointer that need to be unlocked. */
94
- inline static protocol_s *protocol_try_lock(intptr_t fd,
95
- enum facil_protocol_lock_e type) {
96
- errno = 0;
97
- if (spn_trylock(&fd_data(fd).lock))
98
- goto would_block;
99
- protocol_s *pr = fd_data(fd).protocol;
100
- if (!pr) {
101
- spn_unlock(&fd_data(fd).lock);
102
- errno = EBADF;
103
- return NULL;
104
- }
105
- if (spn_trylock(&prt_meta(pr).locks[type])) {
106
- spn_unlock(&fd_data(fd).lock);
107
- goto would_block;
108
- }
109
- spn_unlock(&fd_data(fd).lock);
110
- return pr;
111
- would_block:
112
- errno = EWOULDBLOCK;
113
- return NULL;
114
- }
115
- /** See `facil_protocol_try_lock` for details. */
116
- inline static void protocol_unlock(protocol_s *pr,
117
- enum facil_protocol_lock_e type) {
118
- spn_unlock(&prt_meta(pr).locks[type]);
119
- }
120
-
121
- /* *****************************************************************************
122
- Internal Protocol Names
123
- ***************************************************************************** */
124
- static const char *LISTENER_PROTOCOL_NAME =
125
- "listening protocol __facil_internal__";
126
-
127
- static const char *CONNECTOR_PROTOCOL_NAME = "connect protocol __internal__";
128
-
129
- static const char *TIMER_PROTOCOL_NAME = "timer protocol __facil_internal__";
130
-
131
- static const char *CLUSTER_LISTEN_PROTOCOL_NAME =
132
- "cluster listening protocol __facil_internal__";
133
-
134
- static const char *CLUSTER_CONNECTION_PROTOCOL_NAME =
135
- "cluster connection __facil_internal__";
136
-
137
- static inline int is_counted_protocol(protocol_s *p) {
138
- return p && p->service != TIMER_PROTOCOL_NAME &&
139
- p->service != CLUSTER_LISTEN_PROTOCOL_NAME &&
140
- p->service != CLUSTER_CONNECTION_PROTOCOL_NAME;
141
- }
142
-
143
- /* *****************************************************************************
144
- Event deferring (declarations)
145
- ***************************************************************************** */
146
-
147
- static void deferred_on_close(void *uuid_, void *pr_);
148
- static void deferred_on_shutdown(void *arg, void *arg2);
149
- static void deferred_on_ready(void *arg, void *arg2);
150
- static void deferred_on_data(void *uuid, void *arg2);
151
- static void deferred_ping(void *arg, void *arg2);
152
-
153
- /* *****************************************************************************
154
- Overriding `defer` to use `evio` when waiting for events
155
- ***************************************************************************** */
156
-
157
- /* if the FIO_DEDICATED_SYSTEM is defined threads are activated more often. */
158
- #if FIO_DEDICATED_SYSTEM
159
- void defer_thread_wait(pool_pt pool, void *p_thr) {
160
- (void)pool;
161
- (void)p_thr;
162
- evio_wait(EVIO_TICK);
163
- }
164
- #endif
165
-
166
- /* *****************************************************************************
167
- Event Handlers (evio)
168
- ***************************************************************************** */
169
- void sock_flush_defer(void *arg, void *ignored) {
170
- (void)ignored;
171
- switch (sock_flush((intptr_t)arg)) {
172
- case 1:
173
- evio_add_write(sock_uuid2fd((intptr_t)arg), (void *)arg);
174
- break;
175
- case 0:
176
- defer(deferred_on_ready, arg, NULL);
177
- break;
178
- }
179
- }
180
-
181
- void evio_on_ready(void *arg) { defer(sock_flush_defer, arg, NULL); }
182
- void evio_on_close(void *arg) { sock_force_close((intptr_t)arg); }
183
- void evio_on_error(void *arg) { sock_force_close((intptr_t)arg); }
184
- void evio_on_data(void *arg) { defer(deferred_on_data, arg, NULL); }
185
-
186
- /* *****************************************************************************
187
- Mock Protocol Callbacks and Service Funcions
188
- ***************************************************************************** */
189
- static void mock_on_ev(intptr_t uuid, protocol_s *protocol) {
190
- (void)uuid;
191
- (void)protocol;
192
- }
193
-
194
- static void mock_on_data(intptr_t uuid, protocol_s *protocol) {
195
- facil_quite(uuid);
196
- (void)protocol;
197
- }
198
-
199
- static void mock_on_close(intptr_t uuid, protocol_s *protocol) {
200
- (void)(protocol);
201
- (void)(uuid);
202
- }
203
-
204
- static void mock_on_finish(intptr_t uuid, void *udata) {
205
- (void)(udata);
206
- (void)(uuid);
207
- }
208
-
209
- static void mock_ping(intptr_t uuid, protocol_s *protocol) {
210
- (void)(protocol);
211
- sock_force_close(uuid);
212
- }
213
-
214
- static void mock_idle(void) {}
215
-
216
- /* Support for the default pub/sub cluster engine */
217
- #pragma weak pubsub_cluster_init
218
- void pubsub_cluster_init(void) {}
219
- #pragma weak pubsub_cluster_on_fork_start
220
- void pubsub_cluster_on_fork_start(void) {}
221
- #pragma weak pubsub_cluster_on_fork_end
222
- void pubsub_cluster_on_fork_end(void) {}
223
- #pragma weak pubsub_cluster_cleanup
224
- void pubsub_cluster_cleanup(void) {}
225
-
226
- /* other cleanup concern */
227
- #pragma weak fio_cli_end
228
- void fio_cli_end(void) {}
229
-
230
- /* perform initialization for external services. */
231
- static void facil_external_init(void) {
232
- sock_on_fork();
233
- pubsub_cluster_on_fork_start();
234
- fio_malloc_after_fork();
235
- defer_on_fork();
236
- }
237
-
238
- /* perform stage 2 initialization for external services. */
239
- static void facil_external_init2(void) { pubsub_cluster_on_fork_end(); }
240
-
241
- /* perform cleanup for external services. */
242
- static void facil_external_cleanup(void) {}
243
-
244
- #pragma weak http_lib_init
245
- void http_lib_init(void) {}
246
- #pragma weak http_lib_cleanup
247
- void http_lib_cleanup(void) {}
248
-
249
- /* perform initialization for external services. */
250
- static void facil_external_root_init(void) {
251
- http_lib_init();
252
- pubsub_cluster_init();
253
- }
254
- /* perform cleanup for external services. */
255
- static void facil_external_root_cleanup(void) {
256
- http_lib_cleanup();
257
- pubsub_cluster_cleanup();
258
- fio_cli_end();
259
- }
260
-
261
- /* *****************************************************************************
262
- Deferred event handlers
263
- ***************************************************************************** */
264
- static void deferred_on_close(void *uuid_, void *pr_) {
265
- protocol_s *pr = pr_;
266
- if (pr->rsv)
267
- goto postpone;
268
- pr->on_close((intptr_t)uuid_, pr);
269
- return;
270
- postpone:
271
- defer(deferred_on_close, uuid_, pr_);
272
- }
273
-
274
- static void deferred_on_shutdown(void *arg, void *arg2) {
275
- if (!uuid_data(arg).protocol) {
276
- return;
277
- }
278
- protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_TASK);
279
- if (!pr) {
280
- if (errno == EBADF)
281
- return;
282
- goto postpone;
283
- }
284
- uuid_data(arg).active = facil_data->last_cycle.tv_sec;
285
- uuid_data(arg).timeout = 8;
286
- /* TODO: 0.7.0 catch the return valuse to set timeout and maybe keep open */
287
- pr->on_shutdown((intptr_t)arg, pr);
288
- pr->ping = mock_ping;
289
- protocol_unlock(pr, FIO_PR_LOCK_TASK);
290
- sock_close((intptr_t)arg);
291
- return;
292
- postpone:
293
- defer(deferred_on_shutdown, arg, NULL);
294
- (void)arg2;
295
- }
296
-
297
- static void deferred_on_ready(void *arg, void *arg2) {
298
- if (!uuid_data(arg).protocol) {
299
- return;
300
- }
301
- protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
302
- if (!pr) {
303
- if (errno == EBADF)
304
- return;
305
- goto postpone;
306
- }
307
- pr->on_ready((intptr_t)arg, pr);
308
- protocol_unlock(pr, FIO_PR_LOCK_WRITE);
309
- return;
310
- postpone:
311
- defer(deferred_on_ready, arg, NULL);
312
- (void)arg2;
313
- }
314
-
315
- static void deferred_on_data(void *uuid, void *arg2) {
316
- if (!uuid_data(uuid).protocol || sock_isclosed((intptr_t)uuid)) {
317
- return;
318
- }
319
- protocol_s *pr = protocol_try_lock(sock_uuid2fd(uuid), FIO_PR_LOCK_TASK);
320
- if (!pr) {
321
- if (errno == EBADF)
322
- return;
323
- goto postpone;
324
- }
325
- spn_unlock(&uuid_data(uuid).scheduled);
326
- pr->on_data((intptr_t)uuid, pr);
327
- protocol_unlock(pr, FIO_PR_LOCK_TASK);
328
- if (!spn_trylock(&uuid_data(uuid).scheduled)) {
329
- evio_add_read(sock_uuid2fd((intptr_t)uuid), uuid);
330
- }
331
- return;
332
- postpone:
333
- if (arg2) {
334
- /* the event is being forced, so force rescheduling */
335
- defer(deferred_on_data, (void *)uuid, (void *)1);
336
- } else {
337
- /* the protocol was locked, so there might not be any need for the event */
338
- evio_add_read(sock_uuid2fd((intptr_t)uuid), uuid);
339
- }
340
- return;
341
- }
342
-
343
- static void deferred_ping(void *arg, void *arg2) {
344
- if (!uuid_data(arg).protocol ||
345
- (uuid_data(arg).timeout &&
346
- (uuid_data(arg).timeout >
347
- (facil_data->last_cycle.tv_sec - uuid_data(arg).active)))) {
348
- return;
349
- }
350
- protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
351
- if (!pr)
352
- goto postpone;
353
- pr->ping((intptr_t)arg, pr);
354
- protocol_unlock(pr, FIO_PR_LOCK_WRITE);
355
- return;
356
- postpone:
357
- defer(deferred_ping, arg, NULL);
358
- (void)arg2;
359
- }
360
-
361
- /* *****************************************************************************
362
- Forcing IO events
363
- ***************************************************************************** */
364
-
365
- void facil_force_event(intptr_t uuid, enum facil_io_event ev) {
366
- switch (ev) {
367
- case FIO_EVENT_ON_DATA:
368
- spn_trylock(&uuid_data(uuid).scheduled);
369
- defer(deferred_on_data, (void *)uuid, (void *)1);
370
- break;
371
- case FIO_EVENT_ON_TIMEOUT:
372
- defer(deferred_ping, (void *)uuid, NULL);
373
- break;
374
- case FIO_EVENT_ON_READY:
375
- evio_on_ready((void *)uuid);
376
- break;
377
- }
378
- }
379
-
380
- /**
381
- * Temporarily prevents `on_data` events from firing.
382
- *
383
- * The `on_data` event will be automatically rescheduled when (if) the socket's
384
- * outgoing buffer fills up or when `facil_force_event` is called with
385
- * `FIO_EVENT_ON_DATA`.
386
- */
387
- void facil_quite(intptr_t uuid) {
388
- if (sock_isvalid(uuid))
389
- spn_trylock(&uuid_data(uuid).scheduled);
390
- }
391
-
392
- /* *****************************************************************************
393
- Socket callbacks
394
- ***************************************************************************** */
395
-
396
- void sock_on_close(intptr_t uuid) {
397
- // fprintf(stderr, "INFO: facil.io, on-close called for %u (set to %p)\n",
398
- // (unsigned int)sock_uuid2fd(uuid), (void
399
- // *)uuid_data(uuid).protocol);
400
- spn_lock(&uuid_data(uuid).lock);
401
- protocol_s *old_protocol = uuid_data(uuid).protocol;
402
- uuid_data(uuid) = (struct connection_data_s){.lock = uuid_data(uuid).lock};
403
- spn_unlock(&uuid_data(uuid).lock);
404
- if (old_protocol) {
405
- if (is_counted_protocol(old_protocol)) {
406
- spn_sub(&facil_data->connection_count, 1);
407
- }
408
- defer(deferred_on_close, (void *)uuid, old_protocol);
409
- }
410
- }
411
-
412
- void sock_touch(intptr_t uuid) {
413
- if (facil_data && facil_data->active)
414
- uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
415
- }
416
-
417
- /* *****************************************************************************
418
- Initialization and Cleanup
419
- ***************************************************************************** */
420
- static spn_lock_i facil_libinit_lock = SPN_LOCK_INIT;
421
-
422
- /** Rounds up any size to the nearest page alignment (assumes 4096 bytes per
423
- * page) */
424
- #define round_size(size) (((size) & (~4095)) + (4096 * (!!((size)&4095))))
425
-
426
- static void facil_cluster_cleanup(void); /* cluster data cleanup */
427
-
428
- static void facil_libcleanup(void) {
429
- /* free memory */
430
- spn_lock(&facil_libinit_lock);
431
- if (facil_data) {
432
- facil_external_root_cleanup();
433
- facil_cluster_cleanup();
434
- // defer_perform(); /* perform any lingering cleanup tasks? */
435
- size_t mem_size = sizeof(*facil_data) + ((size_t)facil_data->capacity *
436
- sizeof(struct connection_data_s));
437
- munmap(facil_data, round_size(mem_size));
438
- facil_data = NULL;
439
- }
440
- spn_unlock(&facil_libinit_lock);
441
- }
442
-
443
- static void facil_lib_init(void) {
444
- ssize_t capa = sock_max_capacity();
445
- if (capa < 0) {
446
- perror("ERROR: socket capacity unknown / failure");
447
- exit(ENOMEM);
448
- }
449
- size_t mem_size =
450
- sizeof(*facil_data) + ((size_t)capa * sizeof(struct connection_data_s));
451
- spn_lock(&facil_libinit_lock);
452
- if (facil_data)
453
- goto finish;
454
- facil_data = mmap(NULL, round_size(mem_size), PROT_READ | PROT_WRITE,
455
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
456
- if (!facil_data || facil_data == MAP_FAILED) {
457
- perror("ERROR: Couldn't initialize the facil.io library");
458
- exit(0);
459
- }
460
- memset(facil_data, 0, mem_size);
461
- *facil_data = (struct facil_data_s){
462
- .capacity = capa, .parent = getpid(),
463
- };
464
- facil_external_root_init();
465
- atexit(facil_libcleanup);
466
- #ifdef DEBUG
467
- if (FACIL_PRINT_STATE)
468
- fprintf(stderr,
469
- "Initialized the facil.io library.\n"
470
- "facil.io's memory footprint per connection == %lu Bytes X %lu\n"
471
- "=== facil.io's memory footprint: %lu ===\n\n",
472
- (unsigned long)sizeof(struct connection_data_s),
473
- (unsigned long)facil_data->capacity, (unsigned long)mem_size);
474
- #endif
475
- finish:
476
- spn_unlock(&facil_libinit_lock);
477
- clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
478
- }
479
-
480
- /** Sets to shutdown flag for the current process.
481
- *
482
- * If a cluster is used and this function is called for a worker, a new worker
483
- * will respawn.
484
- *
485
- * This MUST be signal safe (don't call any functions, just uswe flags).
486
- */
487
- static void facil_stop(void) {
488
- if (!facil_data)
489
- return;
490
- facil_data->active = 0;
491
- }
492
-
493
- /* *****************************************************************************
494
- The listenning protocol
495
- ***************************************************************************** */
496
- #undef facil_listen
497
-
498
- struct ListenerProtocol {
499
- protocol_s protocol;
500
- void (*on_open)(void *uuid, void *udata);
501
- void *udata;
502
- void (*on_start)(intptr_t uuid, void *udata);
503
- void (*on_finish)(intptr_t uuid, void *udata);
504
- char *port;
505
- char *address;
506
- uint8_t quite;
507
- };
508
-
509
- static void listener_ping(intptr_t uuid, protocol_s *plistener) {
510
- // fprintf(stderr, "*** Listener Ping Called for %ld\n", sock_uuid2fd(uuid));
511
- uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
512
- return;
513
- (void)plistener;
514
- }
515
-
516
- static void listener_on_data(intptr_t uuid, protocol_s *plistener) {
517
- for (int i = 0; i < 4; ++i) {
518
- intptr_t new_client = sock_accept(uuid);
519
- if (new_client == -1) {
520
- if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ECONNABORTED ||
521
- errno == ECONNRESET)
522
- return;
523
- perror("ERROR: socket accept error");
524
- return;
525
- }
526
- // to defer or not to defer...? TODO: answer the question
527
- struct ListenerProtocol *listener = (struct ListenerProtocol *)plistener;
528
- defer(listener->on_open, (void *)new_client, listener->udata);
529
- }
530
- facil_force_event(uuid, FIO_EVENT_ON_DATA);
531
- }
532
-
533
- static void free_listenner(void *li) { free(li); }
534
-
535
- static void listener_on_close(intptr_t uuid, protocol_s *plistener) {
536
- struct ListenerProtocol *listener = (void *)plistener;
537
- listener->on_finish(uuid, listener->udata);
538
- if (FACIL_PRINT_STATE && facil_data->parent == getpid()) {
539
- if (listener->port) {
540
- fprintf(stderr, "* Stopped listening on port %s\n", listener->port);
541
- } else {
542
- fprintf(stderr, "* Stopped listening on Unix Socket %s\n",
543
- listener->address);
544
- }
545
- }
546
- if (!listener->port) {
547
- unlink(listener->address);
548
- }
549
- free_listenner(listener);
550
- }
551
-
552
- static inline struct ListenerProtocol *
553
- listener_alloc(struct facil_listen_args settings) {
554
- if (!settings.on_start)
555
- settings.on_start = mock_on_finish;
556
- if (!settings.on_finish)
557
- settings.on_finish = mock_on_finish;
558
- size_t port_len = 0;
559
- size_t addr_len = 0;
560
- if (settings.port) {
561
- port_len = strlen(settings.port) + 1;
562
- }
563
- if (settings.address) {
564
- addr_len = strlen(settings.address) + 1;
565
- }
566
- struct ListenerProtocol *listener =
567
- malloc(sizeof(*listener) + addr_len + port_len);
568
-
569
- if (listener) {
570
- *listener = (struct ListenerProtocol){
571
- .protocol.service = LISTENER_PROTOCOL_NAME,
572
- .protocol.on_data = listener_on_data,
573
- .protocol.on_close = listener_on_close,
574
- .protocol.ping = listener_ping,
575
- .on_open = (void (*)(void *, void *))settings.on_open,
576
- .udata = settings.udata,
577
- .on_start = settings.on_start,
578
- .on_finish = settings.on_finish,
579
- };
580
- if (settings.port) {
581
- listener->port = (char *)(listener + 1);
582
- memcpy(listener->port, settings.port, port_len);
583
- }
584
- if (settings.address) {
585
- listener->address = (char *)(listener + 1);
586
- listener->address += port_len;
587
- memcpy(listener->address, settings.address, addr_len);
588
- }
589
- return listener;
590
- }
591
- return NULL;
592
- }
593
-
594
- inline static void listener_on_start(int fd) {
595
- intptr_t uuid = sock_fd2uuid((int)fd);
596
- if (uuid < 0) {
597
- fprintf(stderr, "ERROR: listening socket dropped?\n");
598
- kill(0, SIGINT);
599
- exit(4);
600
- }
601
- if (evio_add(fd, (void *)uuid) < 0) {
602
- perror("Couldn't register listening socket");
603
- kill(0, SIGINT);
604
- exit(4);
605
- }
606
- fd_data(fd).active = facil_data->last_cycle.tv_sec;
607
- // call the on_init callback
608
- struct ListenerProtocol *listener =
609
- (struct ListenerProtocol *)uuid_data(uuid).protocol;
610
- listener->on_start(uuid, listener->udata);
611
- }
612
-
613
- /**
614
- Listens to a server with the following server settings (which MUST include
615
- a default protocol).
616
-
617
- This method blocks the current thread until the server is stopped (either
618
- though a `srv_stop` function or when a SIGINT/SIGTERM is received).
619
- */
620
- int facil_listen(struct facil_listen_args settings) {
621
- if (!facil_data)
622
- facil_lib_init();
623
- if (settings.on_open == NULL) {
624
- errno = EINVAL;
625
- return -1;
626
- }
627
- if (!settings.port || settings.port[0] == 0 ||
628
- (settings.port[0] == '0' && settings.port[1] == 0)) {
629
- settings.port = NULL;
630
- }
631
- intptr_t uuid = sock_listen(settings.address, settings.port);
632
- if (uuid == -1) {
633
- return -1;
634
- }
635
- protocol_s *protocol = (void *)listener_alloc(settings);
636
- facil_attach(uuid, protocol);
637
- if (!protocol) {
638
- sock_close(uuid);
639
- return -1;
640
- }
641
- if (FACIL_PRINT_STATE && facil_data->parent == getpid()) {
642
- if (settings.port)
643
- fprintf(stderr, "* Listening on port %s\n", settings.port);
644
- else
645
- fprintf(stderr, "* Listening on Unix Socket at %s\n", settings.address);
646
- }
647
- return 0;
648
- }
649
-
650
- /* *****************************************************************************
651
- Connect (as client)
652
- ***************************************************************************** */
653
-
654
- struct ConnectProtocol {
655
- protocol_s protocol;
656
- void (*on_connect)(void *uuid, void *udata);
657
- void (*on_fail)(intptr_t uuid, void *udata);
658
- void *udata;
659
- intptr_t uuid;
660
- uint8_t opened;
661
- };
662
-
663
- /* The first `ready` signal is fired when a connection was established */
664
- static void connector_on_ready(intptr_t uuid, protocol_s *_connector) {
665
- struct ConnectProtocol *connector = (void *)_connector;
666
- sock_touch(uuid);
667
- if (connector->opened == 0) {
668
- connector->opened = 1;
669
- facil_set_timeout(uuid, 0); /* remove connection timeout settings */
670
- connector->on_connect((void *)uuid, connector->udata);
671
- evio_add_write(sock_uuid2fd(uuid), (void *)uuid);
672
- }
673
- return;
674
- }
675
-
676
- /* If data events reach this protocol, delay their execution. */
677
- static void connector_on_data(intptr_t uuid, protocol_s *connector) {
678
- (void)connector;
679
- (void)uuid;
680
- }
681
-
682
- /* Failed to connect */
683
- static void connector_on_close(intptr_t uuid, protocol_s *pconnector) {
684
- struct ConnectProtocol *connector = (void *)pconnector;
685
- if (connector->opened == 0 && connector->on_fail)
686
- connector->on_fail(connector->uuid, connector->udata);
687
- free(connector);
688
- (void)uuid;
689
- }
690
-
691
- #undef facil_connect
692
- intptr_t facil_connect(struct facil_connect_args opt) {
693
- intptr_t uuid = -1;
694
- if (!opt.on_connect || (!opt.address && !opt.port))
695
- goto error;
696
- if (!opt.timeout)
697
- opt.timeout = 30;
698
- struct ConnectProtocol *connector = malloc(sizeof(*connector));
699
- if (!connector)
700
- goto error;
701
- *connector = (struct ConnectProtocol){
702
- .on_connect = (void (*)(void *, void *))opt.on_connect,
703
- .on_fail = opt.on_fail,
704
- .udata = opt.udata,
705
- .protocol.service = CONNECTOR_PROTOCOL_NAME,
706
- .protocol.on_data = connector_on_data,
707
- .protocol.on_ready = connector_on_ready,
708
- .protocol.on_close = connector_on_close,
709
- .opened = 0,
710
- };
711
- uuid = connector->uuid = sock_connect(opt.address, opt.port);
712
- /* check for errors, always invoke the on_fail if required */
713
- if (uuid == -1) {
714
- free(connector);
715
- goto error;
716
- }
717
- if (facil_attach(uuid, &connector->protocol) == -1) {
718
- /* facil_attach calls the failure / `on_close` callback */
719
- sock_close(uuid);
720
- return -1;
721
- }
722
- facil_set_timeout(uuid, opt.timeout);
723
- return uuid;
724
- error:
725
- if (opt.on_fail)
726
- opt.on_fail(uuid, opt.udata);
727
- return -1;
728
- }
729
- #define facil_connect(...) \
730
- facil_connect((struct facil_connect_args){__VA_ARGS__})
731
-
732
- /* *****************************************************************************
733
- Timers
734
- ***************************************************************************** */
735
-
736
- /* *******
737
- Timer Protocol
738
- ******* */
739
- typedef struct {
740
- protocol_s protocol;
741
- size_t milliseconds;
742
- size_t repetitions;
743
- void (*task)(void *);
744
- void (*on_finish)(void *);
745
- void *arg;
746
- } timer_protocol_s;
747
-
748
- #define prot2timer(protocol) (*((timer_protocol_s *)(protocol)))
749
-
750
- static void timer_on_data(intptr_t uuid, protocol_s *protocol) {
751
- prot2timer(protocol).task(prot2timer(protocol).arg);
752
- if (prot2timer(protocol).repetitions == 0)
753
- goto reschedule;
754
- prot2timer(protocol).repetitions -= 1;
755
- if (prot2timer(protocol).repetitions)
756
- goto reschedule;
757
- sock_force_close(uuid);
758
- return;
759
- reschedule:
760
- spn_trylock(&uuid_data(uuid).scheduled);
761
- evio_set_timer(sock_uuid2fd(uuid), (void *)uuid,
762
- prot2timer(protocol).milliseconds);
763
- }
764
-
765
- static void timer_on_close(intptr_t uuid, protocol_s *protocol) {
766
- prot2timer(protocol).on_finish(prot2timer(protocol).arg);
767
- free(protocol);
768
- (void)uuid;
769
- }
770
-
771
- static void timer_ping(intptr_t uuid, protocol_s *protocol) {
772
- sock_touch(uuid);
773
- (void)protocol;
774
- }
775
-
776
- static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
777
- size_t milliseconds,
778
- size_t repetitions,
779
- void (*on_finish)(void *)) {
780
- if (!on_finish)
781
- on_finish = (void (*)(void *))mock_on_close;
782
- timer_protocol_s *t = malloc(sizeof(*t));
783
- if (t)
784
- *t = (timer_protocol_s){
785
- .protocol.service = TIMER_PROTOCOL_NAME,
786
- .protocol.on_data = timer_on_data,
787
- .protocol.on_close = timer_on_close,
788
- .protocol.ping = timer_ping,
789
- .arg = arg,
790
- .task = task,
791
- .on_finish = on_finish,
792
- .milliseconds = milliseconds,
793
- .repetitions = repetitions,
794
- };
795
- return t;
796
- }
797
-
798
- inline static void timer_on_server_start(int fd) {
799
- if (evio_set_timer(fd, (void *)sock_fd2uuid(fd),
800
- prot2timer(fd_data(fd).protocol).milliseconds)) {
801
- perror("Couldn't register a required timed event.");
802
- kill(0, SIGINT);
803
- exit(4);
804
- }
805
- }
806
-
807
- /**
808
- * Creates a system timer (at the cost of 1 file descriptor).
809
- *
810
- * The task will repeat `repetitions` times. If `repetitions` is set to 0, task
811
- * will repeat forever.
812
- *
813
- * Returns -1 on error or the new file descriptor on succeess.
814
- *
815
- * The `on_finish` handler is always called (even on error).
816
- */
817
- int facil_run_every(size_t milliseconds, size_t repetitions,
818
- void (*task)(void *), void *arg,
819
- void (*on_finish)(void *)) {
820
- if (task == NULL)
821
- goto error_fin;
822
- timer_protocol_s *protocol = NULL;
823
- intptr_t uuid = -1;
824
- int fd = evio_open_timer();
825
- if (fd == -1) {
826
- perror("ERROR: couldn't create a timer fd");
827
- goto error;
828
- }
829
- uuid = sock_open(fd);
830
- if (uuid == -1)
831
- goto error;
832
- protocol = timer_alloc(task, arg, milliseconds, repetitions, on_finish);
833
- if (protocol == NULL)
834
- goto error;
835
- facil_attach(uuid, (protocol_s *)protocol);
836
- if (evio_isactive() && evio_set_timer(fd, (void *)uuid, milliseconds) == -1) {
837
- /* don't goto error because the protocol is attached. */
838
- const int old = errno;
839
- sock_force_close(uuid);
840
- errno = old;
841
- return -1;
842
- }
843
- return 0;
844
- error:
845
- if (uuid != -1) {
846
- const int old = errno;
847
- sock_close(uuid);
848
- errno = old;
849
- } else if (fd != -1) {
850
- const int old = errno;
851
- close(fd);
852
- errno = old;
853
- }
854
- error_fin:
855
- if (on_finish) {
856
- const int old = errno;
857
- on_finish(arg);
858
- errno = old;
859
- }
860
- return -1;
861
- }
862
-
863
- /* *****************************************************************************
864
- Cluster Messaging - using Unix Sockets
865
- ***************************************************************************** */
866
-
867
- #ifdef __BIG_ENDIAN__
868
- inline static uint32_t cluster_str2uint32(uint8_t *str) {
869
- return ((str[0] & 0xFF) | ((((uint32_t)str[1]) << 8) & 0xFF00) |
870
- ((((uint32_t)str[2]) << 16) & 0xFF0000) |
871
- ((((uint32_t)str[3]) << 24) & 0xFF000000));
872
- }
873
- inline static void cluster_uint2str(uint8_t *dest, uint32_t i) {
874
- dest[0] = i & 0xFF;
875
- dest[1] = (i >> 8) & 0xFF;
876
- dest[2] = (i >> 16) & 0xFF;
877
- dest[3] = (i >> 24) & 0xFF;
878
- }
879
- #else
880
- inline static uint32_t cluster_str2uint32(uint8_t *str) {
881
- return (((((uint32_t)str[0]) << 24) & 0xFF000000) |
882
- ((((uint32_t)str[1]) << 16) & 0xFF0000) |
883
- ((((uint32_t)str[2]) << 8) & 0xFF00) | (str[3] & 0xFF));
884
- }
885
- inline static void cluster_uint2str(uint8_t *dest, uint32_t i) {
886
- dest[0] = (i >> 24) & 0xFF;
887
- dest[1] = (i >> 16) & 0xFF;
888
- dest[2] = (i >> 8) & 0xFF;
889
- dest[3] = i & 0xFF;
890
- }
891
- #endif
892
-
893
- #define CLUSTER_READ_BUFFER 16384
894
- typedef struct {
895
- protocol_s pr;
896
- FIOBJ channel;
897
- FIOBJ msg;
898
- uint32_t exp_channel;
899
- uint32_t exp_msg;
900
- uint32_t type;
901
- int32_t filter;
902
- uint32_t length;
903
- uint8_t buffer[];
904
- } cluster_pr_s;
905
-
906
- typedef struct {
907
- void (*on_message)(int32_t filter, FIOBJ, FIOBJ);
908
- FIOBJ channel;
909
- FIOBJ msg;
910
- int32_t filter;
911
- } cluster_msg_data_s;
912
-
913
- static void cluster_on_new_peer(intptr_t srv, protocol_s *pr);
914
- static void cluster_on_listening_ping(intptr_t srv, protocol_s *pr);
915
- static void cluster_on_listening_close(intptr_t srv, protocol_s *pr);
916
-
917
- static struct {
918
- protocol_s listening;
919
- intptr_t root;
920
- fio_hash_s clients;
921
- fio_hash_s handlers;
922
- spn_lock_i lock;
923
- uint8_t client_mode;
924
- char cluster_name[128];
925
- } facil_cluster_data = {
926
- .lock = SPN_LOCK_INIT,
927
- .root = -1,
928
- .listening =
929
- {
930
- .on_close = cluster_on_listening_close,
931
- .ping = cluster_on_listening_ping,
932
- .on_data = cluster_on_new_peer,
933
- },
934
- };
935
-
936
- enum cluster_message_type_e {
937
- CLUSTER_MESSAGE_FORWARD,
938
- CLUSTER_MESSAGE_JSON,
939
- CLUSTER_MESSAGE_SHUTDOWN,
940
- CLUSTER_MESSAGE_ERROR,
941
- CLUSTER_MESSAGE_PING,
942
- };
943
-
944
- static void cluster_deferred_handler(void *msg_data_, void *ignr) {
945
- cluster_msg_data_s *data = msg_data_;
946
- data->on_message(data->filter, data->channel, data->msg);
947
- fiobj_free(data->channel);
948
- fiobj_free(data->msg);
949
- fio_free(data);
950
- (void)ignr;
951
- }
952
-
953
- static void cluster_forward_msg2handlers(cluster_pr_s *c) {
954
- spn_lock(&facil_cluster_data.lock);
955
- void *target_ =
956
- fio_hash_find(&facil_cluster_data.handlers, (FIO_HASH_KEY_TYPE)c->filter);
957
- spn_unlock(&facil_cluster_data.lock);
958
- // fprintf(stderr, "handler for %d: %p\n", c->filter, target_);
959
- if (target_) {
960
- cluster_msg_data_s *data = fio_malloc(sizeof(*data));
961
- if (!data) {
962
- perror("FATAL ERROR: (facil.io cluster) couldn't allocate memory");
963
- exit(errno);
964
- }
965
- *data = (cluster_msg_data_s){
966
- .on_message = ((cluster_msg_data_s *)(&target_))->on_message,
967
- .channel = fiobj_dup(c->channel),
968
- .msg = fiobj_dup(c->msg),
969
- .filter = c->filter,
970
- };
971
- defer(cluster_deferred_handler, data, NULL);
972
- }
973
- }
974
-
975
- static inline FIOBJ cluster_wrap_message(uint32_t ch_len, uint32_t msg_len,
976
- uint32_t type, int32_t id,
977
- uint8_t *ch_data, uint8_t *msg_data) {
978
- FIOBJ buf = fiobj_str_buf(ch_len + msg_len + 16);
979
- fio_cstr_s f = fiobj_obj2cstr(buf);
980
- cluster_uint2str(f.bytes, ch_len);
981
- cluster_uint2str(f.bytes + 4, msg_len);
982
- cluster_uint2str(f.bytes + 8, type);
983
- cluster_uint2str(f.bytes + 12, (uint32_t)id);
984
- if (ch_len && ch_data) {
985
- memcpy(f.bytes + 16, ch_data, ch_len);
986
- }
987
- if (msg_len && msg_data) {
988
- memcpy(f.bytes + 16 + ch_len, msg_data, msg_len);
989
- }
990
- fiobj_str_resize(buf, ch_len + msg_len + 16);
991
- return buf;
992
- }
993
-
994
- static inline void cluster_send2clients(uint32_t ch_len, uint32_t msg_len,
995
- uint32_t type, int32_t id,
996
- uint8_t *ch_data, uint8_t *msg_data,
997
- intptr_t uuid) {
998
- if (facil_cluster_data.clients.count == 0)
999
- return;
1000
- FIOBJ forward =
1001
- cluster_wrap_message(ch_len, msg_len, type, id, ch_data, msg_data);
1002
- spn_lock(&facil_cluster_data.lock);
1003
- FIO_HASH_FOR_LOOP(&facil_cluster_data.clients, i) {
1004
- if (i->obj) {
1005
- if ((intptr_t)i->key != uuid)
1006
- fiobj_send_free((intptr_t)i->key, fiobj_dup(forward));
1007
- }
1008
- }
1009
- spn_unlock(&facil_cluster_data.lock);
1010
- fiobj_free(forward);
1011
- }
1012
-
1013
- static inline void cluster_send2traget(uint32_t ch_len, uint32_t msg_len,
1014
- uint32_t type, int32_t id,
1015
- uint8_t *ch_data, uint8_t *msg_data) {
1016
- if (facil_cluster_data.client_mode) {
1017
- FIOBJ forward =
1018
- cluster_wrap_message(ch_len, msg_len, type, id, ch_data, msg_data);
1019
- fiobj_send_free(facil_cluster_data.root, forward);
1020
- } else {
1021
- cluster_send2clients(ch_len, msg_len, type, id, ch_data, msg_data, 0);
1022
- }
1023
- }
1024
-
1025
- /* NOT signal safe. */
1026
- static inline void facil_cluster_signal_children(void) {
1027
- if (facil_parent_pid() != getpid()) {
1028
- facil_stop();
1029
- return;
1030
- }
1031
- cluster_send2traget(0, 0, CLUSTER_MESSAGE_SHUTDOWN, 0, NULL, NULL);
1032
- /* TODO: add `on_restart` callback handler. */
1033
- }
1034
-
1035
- static void cluster_on_client_message(cluster_pr_s *c, intptr_t uuid) {
1036
- switch ((enum cluster_message_type_e)c->type) {
1037
- case CLUSTER_MESSAGE_JSON: {
1038
- fio_cstr_s s = fiobj_obj2cstr(c->channel);
1039
- FIOBJ tmp = FIOBJ_INVALID;
1040
- if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
1041
- fiobj_free(c->channel);
1042
- c->channel = tmp;
1043
- tmp = FIOBJ_INVALID;
1044
- } else {
1045
- fprintf(stderr,
1046
- "WARNING: (facil.io cluster) JSON channel isn't valid JSON.\n");
1047
- }
1048
- s = fiobj_obj2cstr(c->msg);
1049
- if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
1050
- fiobj_free(c->msg);
1051
- c->msg = tmp;
1052
- } else {
1053
- fprintf(stderr,
1054
- "WARNING: (facil.io cluster) JSON message isn't valid JSON.\n");
1055
- }
1056
- }
1057
- /* fallthrough */
1058
- case CLUSTER_MESSAGE_FORWARD:
1059
- cluster_forward_msg2handlers(c);
1060
- break;
1061
-
1062
- case CLUSTER_MESSAGE_ERROR:
1063
- case CLUSTER_MESSAGE_SHUTDOWN:
1064
- facil_stop();
1065
- facil_cluster_data.root = -1;
1066
- sock_close(uuid);
1067
- break;
1068
-
1069
- case CLUSTER_MESSAGE_PING:
1070
- /* do nothing, really. */
1071
- break;
1072
- }
1073
- }
1074
-
1075
- static void cluster_on_server_message(cluster_pr_s *c, intptr_t uuid) {
1076
- switch ((enum cluster_message_type_e)c->type) {
1077
- case CLUSTER_MESSAGE_JSON:
1078
- case CLUSTER_MESSAGE_FORWARD: {
1079
- if (fio_hash_count(&facil_cluster_data.clients)) {
1080
- fio_cstr_s cs = fiobj_obj2cstr(c->channel);
1081
- fio_cstr_s ms = fiobj_obj2cstr(c->msg);
1082
- cluster_send2clients((uint32_t)cs.len, (uint32_t)ms.len, c->type,
1083
- c->filter, cs.bytes, ms.bytes, uuid);
1084
- }
1085
- if (c->type == CLUSTER_MESSAGE_JSON) {
1086
- fio_cstr_s s = fiobj_obj2cstr(c->channel);
1087
- FIOBJ tmp = FIOBJ_INVALID;
1088
- if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
1089
- fiobj_free(c->channel);
1090
- c->channel = tmp;
1091
- tmp = FIOBJ_INVALID;
1092
- } else {
1093
- fprintf(stderr,
1094
- "WARNING: (facil.io cluster) JSON message isn't valid JSON.\n");
1095
- }
1096
- s = fiobj_obj2cstr(c->msg);
1097
- if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
1098
- fiobj_free(c->msg);
1099
- c->msg = tmp;
1100
- } else {
1101
- fprintf(stderr,
1102
- "WARNING: (facil.io cluster) JSON message isn't valid JSON.\n");
1103
- }
1104
- }
1105
- cluster_forward_msg2handlers(c);
1106
- break;
1107
- }
1108
- case CLUSTER_MESSAGE_SHUTDOWN:
1109
- case CLUSTER_MESSAGE_ERROR:
1110
- case CLUSTER_MESSAGE_PING:
1111
- /* do nothing, really. */
1112
- break;
1113
- }
1114
- }
1115
-
1116
- static void cluster_on_client_close(intptr_t uuid, protocol_s *pr_) {
1117
- cluster_pr_s *c = (cluster_pr_s *)pr_;
1118
- /* no shutdown message received - parent crashed. */
1119
- if (facil_cluster_data.root == uuid && c->type != CLUSTER_MESSAGE_SHUTDOWN &&
1120
- facil_data->active) {
1121
- if (FACIL_PRINT_STATE)
1122
- fprintf(stderr, "* (%d) Parent Process crash detected!\n", getpid());
1123
- unlink(facil_cluster_data.cluster_name);
1124
- }
1125
- fiobj_free(c->msg);
1126
- fiobj_free(c->channel);
1127
- free(c);
1128
- facil_stop();
1129
- if (facil_cluster_data.root == uuid)
1130
- facil_cluster_data.root = -1;
1131
- }
1132
-
1133
- static void cluster_on_server_close(intptr_t uuid, protocol_s *pr_) {
1134
- cluster_pr_s *c = (cluster_pr_s *)pr_;
1135
- if (facil_cluster_data.client_mode || facil_parent_pid() != getpid()) {
1136
- /* we respawned - clean up resources, but don't stop server */
1137
- fiobj_free(c->msg);
1138
- fiobj_free(c->channel);
1139
- free(c);
1140
- if (facil_cluster_data.root == uuid)
1141
- facil_cluster_data.root = -1;
1142
- return;
1143
- }
1144
- spn_lock(&facil_cluster_data.lock);
1145
- fio_hash_insert(&facil_cluster_data.clients, (FIO_HASH_KEY_TYPE)uuid, NULL);
1146
- // fio_hash_compact(&facil_cluster_data.clients);
1147
- spn_unlock(&facil_cluster_data.lock);
1148
- fiobj_free(c->msg);
1149
- fiobj_free(c->channel);
1150
- free(c);
1151
- }
1152
-
1153
- static void cluster_on_shutdown(intptr_t uuid, protocol_s *pr_) {
1154
- cluster_send2traget(0, 0, CLUSTER_MESSAGE_SHUTDOWN, 0, NULL, NULL);
1155
- facil_force_event(uuid, FIO_EVENT_ON_READY);
1156
- (void)pr_;
1157
- (void)uuid;
1158
- }
1159
-
1160
- static void cluster_on_data(intptr_t uuid, protocol_s *pr_) {
1161
- cluster_pr_s *c = (cluster_pr_s *)pr_;
1162
- ssize_t i =
1163
- sock_read(uuid, c->buffer + c->length, CLUSTER_READ_BUFFER - c->length);
1164
- if (i <= 0)
1165
- return;
1166
- c->length += i;
1167
- i = 0;
1168
- do {
1169
- if (!c->exp_channel && !c->exp_msg) {
1170
- if (c->length - i < 16)
1171
- break;
1172
- c->exp_channel = cluster_str2uint32(c->buffer + i);
1173
- c->exp_msg = cluster_str2uint32(c->buffer + i + 4);
1174
- c->type = cluster_str2uint32(c->buffer + i + 8);
1175
- c->filter = (int32_t)cluster_str2uint32(c->buffer + i + 12);
1176
- if (c->exp_channel) {
1177
- if (c->exp_channel >= (1024 * 1024 * 16)) {
1178
- fprintf(
1179
- stderr,
1180
- "ERROR: (%d) cluster message name too long (16Mb limit): %u\n",
1181
- getpid(), (unsigned int)c->exp_channel);
1182
- facil_stop();
1183
- return;
1184
- }
1185
- c->channel = fiobj_str_buf(c->exp_channel);
1186
- }
1187
- if (c->exp_msg) {
1188
- if (c->exp_msg >= (1024 * 1024 * 64)) {
1189
- fprintf(
1190
- stderr,
1191
- "ERROR: (%d) cluster message data too long (64Mb limit): %u\n",
1192
- getpid(), (unsigned int)c->exp_msg);
1193
- facil_stop();
1194
- return;
1195
- }
1196
- c->msg = fiobj_str_buf(c->exp_msg);
1197
- }
1198
- i += 16;
1199
- }
1200
- if (c->exp_channel) {
1201
- if (c->exp_channel + i > c->length) {
1202
- fiobj_str_write(c->channel, (char *)c->buffer + i,
1203
- (size_t)(c->length - i));
1204
- c->exp_channel -= (c->length - i);
1205
- i = c->length;
1206
- break;
1207
- } else {
1208
- fiobj_str_write(c->channel, (char *)c->buffer + i, c->exp_channel);
1209
- i += c->exp_channel;
1210
- c->exp_channel = 0;
1211
- }
1212
- }
1213
- if (c->exp_msg) {
1214
- if (c->exp_msg + i > c->length) {
1215
- fiobj_str_write(c->msg, (char *)c->buffer + i, (size_t)(c->length - i));
1216
- c->exp_msg -= (c->length - i);
1217
- i = c->length;
1218
- break;
1219
- } else {
1220
- fiobj_str_write(c->msg, (char *)c->buffer + i, c->exp_msg);
1221
- i += c->exp_msg;
1222
- c->exp_msg = 0;
1223
- }
1224
- }
1225
- if (facil_cluster_data.client_mode) {
1226
- cluster_on_client_message(c, uuid);
1227
- } else {
1228
- cluster_on_server_message(c, uuid);
1229
- }
1230
- fiobj_free(c->msg);
1231
- fiobj_free(c->channel);
1232
- c->msg = FIOBJ_INVALID;
1233
- c->channel = FIOBJ_INVALID;
1234
- } while (c->length > i);
1235
- c->length -= i;
1236
- if (c->length) {
1237
- memmove(c->buffer, c->buffer + i, c->length);
1238
- }
1239
- (void)pr_;
1240
- }
1241
- static void cluster_ping(intptr_t uuid, protocol_s *pr_) {
1242
- FIOBJ ping = cluster_wrap_message(0, 0, CLUSTER_MESSAGE_PING, 0, NULL, NULL);
1243
- fiobj_send_free(uuid, ping);
1244
- (void)pr_;
1245
- }
1246
-
1247
- static void cluster_on_open(intptr_t fd, void *udata) {
1248
- cluster_pr_s *pr = malloc(sizeof(*pr) + CLUSTER_READ_BUFFER);
1249
- *pr = (cluster_pr_s){
1250
- .pr =
1251
- {
1252
- .service = CLUSTER_CONNECTION_PROTOCOL_NAME,
1253
- .on_data = cluster_on_data,
1254
- .on_shutdown = cluster_on_shutdown,
1255
- .on_close =
1256
- (facil_cluster_data.client_mode ? cluster_on_client_close
1257
- : cluster_on_server_close),
1258
- .ping = cluster_ping,
1259
- },
1260
- };
1261
- if (facil_cluster_data.root >= 0 && facil_cluster_data.root != fd) {
1262
- // if (facil_parent_pid() != getpid()) {
1263
- // fprintf(stderr,
1264
- // "WARNING: non-root process adding client...\n"
1265
- // " adding to clients %p (root == %p)\n",
1266
- // (void *)fd, (void *)facil_cluster_data.root);
1267
- // } else {
1268
- // fprintf(stderr, "INFO: root process adding child connection %p\n",
1269
- // (void *)fd);
1270
- // }
1271
- spn_lock(&facil_cluster_data.lock);
1272
- fio_hash_insert(&facil_cluster_data.clients, (FIO_HASH_KEY_TYPE)fd,
1273
- (void *)fd);
1274
- spn_unlock(&facil_cluster_data.lock);
1275
- } else if (facil_parent_pid() != getpid()) {
1276
- // fprintf(stderr, "INFO: child process registering...%p \n", (void *)fd);
1277
- }
1278
- if (facil_attach(fd, &pr->pr) == -1) {
1279
- fprintf(stderr, "(%d) ", getpid());
1280
- perror("ERROR: (facil.io cluster) couldn't attach connection");
1281
- facil_stop();
1282
- }
1283
- (void)udata;
1284
- }
1285
-
1286
- static void cluster_on_new_peer(intptr_t srv, protocol_s *pr) {
1287
- intptr_t client = sock_accept(srv);
1288
- if (client == -1) {
1289
- // fprintf(stderr,
1290
- // "ERROR: (facil.io cluster) couldn't accept connection\n");
1291
- } else {
1292
- cluster_on_open(client, NULL);
1293
- }
1294
- (void)pr;
1295
- }
1296
- static void cluster_on_listening_close(intptr_t srv, protocol_s *pr) {
1297
- if (facil_parent_pid() == getpid()) {
1298
- unlink(facil_cluster_data.cluster_name);
1299
- }
1300
- if (facil_cluster_data.root == srv)
1301
- facil_cluster_data.root = -1;
1302
- (void)srv;
1303
- (void)pr;
1304
- }
1305
- static void cluster_on_listening_ping(intptr_t srv, protocol_s *pr) {
1306
- sock_touch(srv);
1307
- (void)pr;
1308
- }
1309
-
1310
- static int cluster_on_start(void) {
1311
- if (facil_data->active <= 1)
1312
- return 0;
1313
- if (facil_parent_pid() == getpid()) {
1314
- /* Root process, listen to incoming cluster connections */
1315
- facil_cluster_data.client_mode = 0;
1316
- facil_cluster_data.listening.service = CLUSTER_LISTEN_PROTOCOL_NAME;
1317
- if (facil_attach(facil_cluster_data.root, &facil_cluster_data.listening)) {
1318
- perror("FATAL ERROR: (facil.io) couldn't attach cluster socket");
1319
- }
1320
- } else {
1321
- /* Worker process, connect to Root process */
1322
- facil_cluster_data.client_mode = 1;
1323
- fio_hash_free(&facil_cluster_data.clients);
1324
- facil_cluster_data.clients = (fio_hash_s)FIO_HASH_INIT;
1325
- if (facil_cluster_data.root != -1) {
1326
- sock_force_close(facil_cluster_data.root);
1327
- }
1328
- facil_cluster_data.root =
1329
- facil_connect(.address = facil_cluster_data.cluster_name,
1330
- .on_connect = cluster_on_open);
1331
- if (facil_cluster_data.root == -1) {
1332
- perror(
1333
- "FATAL ERROR: (facil.io cluster) couldn't connect to cluster socket");
1334
- fprintf(stderr, " socket: %s\n", facil_cluster_data.cluster_name);
1335
- facil_stop();
1336
- return -1;
1337
- }
1338
- }
1339
- return 0;
1340
- }
1341
-
1342
- static int facil_cluster_init(void) {
1343
- if (facil_data->active <= 1)
1344
- return 0;
1345
- /* create a unique socket name */
1346
- char *tmp_folder = getenv("TMPDIR");
1347
- uint32_t tmp_folder_len = 0;
1348
- if (!tmp_folder || ((tmp_folder_len = (uint32_t)strlen(tmp_folder)) > 100)) {
1349
- #ifdef P_tmpdir
1350
- tmp_folder = P_tmpdir;
1351
- if (tmp_folder)
1352
- tmp_folder_len = (uint32_t)strlen(tmp_folder);
1353
- #else
1354
- tmp_folder = "/tmp/";
1355
- tmp_folder_len = 5;
1356
- #endif
1357
- }
1358
- if (tmp_folder_len >= 100)
1359
- tmp_folder_len = 0;
1360
- if (tmp_folder_len) {
1361
- memcpy(facil_cluster_data.cluster_name, tmp_folder, tmp_folder_len);
1362
- if (facil_cluster_data.cluster_name[tmp_folder_len - 1] != '/')
1363
- facil_cluster_data.cluster_name[tmp_folder_len++] = '/';
1364
- }
1365
- memcpy(facil_cluster_data.cluster_name + tmp_folder_len, "facil-io-sock-",
1366
- 14);
1367
- tmp_folder_len += 14;
1368
- tmp_folder_len +=
1369
- fio_ltoa(facil_cluster_data.cluster_name + tmp_folder_len, getpid(), 8);
1370
- facil_cluster_data.cluster_name[tmp_folder_len] = 0;
1371
-
1372
- /* remove if existing */
1373
- unlink(facil_cluster_data.cluster_name);
1374
- /* create, bind, listen */
1375
- facil_cluster_data.root = sock_listen(facil_cluster_data.cluster_name, NULL);
1376
-
1377
- if (facil_cluster_data.root == -1) {
1378
- perror("FATAL ERROR: (facil.io cluster) failed to open cluster socket.\n"
1379
- " check file permissions");
1380
- return -1;
1381
- }
1382
- return 0;
1383
- }
1384
-
1385
- void facil_cluster_set_handler(int32_t filter,
1386
- void (*on_message)(int32_t id, FIOBJ ch,
1387
- FIOBJ msg)) {
1388
- spn_lock(&facil_cluster_data.lock);
1389
- fio_hash_insert(&facil_cluster_data.handlers, (uint64_t)filter,
1390
- (void *)(uintptr_t)on_message);
1391
- spn_unlock(&facil_cluster_data.lock);
1392
- }
1393
-
1394
- int facil_cluster_send(int32_t filter, FIOBJ ch, FIOBJ msg) {
1395
- if (!facil_data) {
1396
- fprintf(stderr, "ERROR: cluster inactive, can't send message.\n");
1397
- return -1;
1398
- }
1399
- uint32_t type = CLUSTER_MESSAGE_FORWARD;
1400
-
1401
- if ((!ch || FIOBJ_TYPE_IS(ch, FIOBJ_T_STRING)) &&
1402
- (!msg || FIOBJ_TYPE_IS(msg, FIOBJ_T_STRING))) {
1403
- fiobj_dup(ch);
1404
- fiobj_dup(msg);
1405
- } else {
1406
- type = CLUSTER_MESSAGE_JSON;
1407
- ch = fiobj_obj2json(ch, 0);
1408
- msg = fiobj_obj2json(msg, 0);
1409
- }
1410
- fio_cstr_s cs = fiobj_obj2cstr(ch);
1411
- fio_cstr_s ms = fiobj_obj2cstr(msg);
1412
- cluster_send2traget((uint32_t)cs.len, (uint32_t)ms.len, type, filter,
1413
- cs.bytes, ms.bytes);
1414
- fiobj_free(ch);
1415
- fiobj_free(msg);
1416
- return 0;
1417
- }
1418
-
1419
- static void facil_cluster_cleanup(void) {
1420
- fio_hash_free(&facil_cluster_data.handlers);
1421
- fio_hash_free(&facil_cluster_data.clients);
1422
- }
1423
- /* *****************************************************************************
1424
- Running the server
1425
- ***************************************************************************** */
1426
-
1427
- volatile uint8_t facil_signal_children_flag = 0;
1428
-
1429
- static inline void facil_internal_poll(void) {
1430
- if (facil_signal_children_flag) {
1431
- facil_signal_children_flag = 0;
1432
- facil_cluster_signal_children();
1433
- }
1434
- }
1435
-
1436
- static inline void facil_internal_poll_reset(void) {
1437
- facil_signal_children_flag = 0;
1438
- }
1439
-
1440
- static void print_pid(void *arg, void *ignr) {
1441
- (void)arg;
1442
- (void)ignr;
1443
- fprintf(stderr, "* %d is running.\n", getpid());
1444
- }
1445
-
1446
- static void facil_review_timeout(void *arg, void *ignr) {
1447
- (void)ignr;
1448
- protocol_s *tmp;
1449
- time_t review = facil_data->last_cycle.tv_sec;
1450
- intptr_t fd = (intptr_t)arg;
1451
-
1452
- uint16_t timeout = fd_data(fd).timeout;
1453
- if (!timeout)
1454
- timeout = 300; /* enforced timout settings */
1455
-
1456
- if (!fd_data(fd).protocol || (fd_data(fd).active + timeout >= review))
1457
- goto finish;
1458
- tmp = protocol_try_lock(fd, FIO_PR_LOCK_STATE);
1459
- if (!tmp)
1460
- goto reschedule;
1461
- if (prt_meta(tmp).locks[FIO_PR_LOCK_TASK] ||
1462
- prt_meta(tmp).locks[FIO_PR_LOCK_WRITE])
1463
- goto unlock;
1464
- defer(deferred_ping, (void *)sock_fd2uuid((int)fd), NULL);
1465
- unlock:
1466
- protocol_unlock(tmp, FIO_PR_LOCK_STATE);
1467
- finish:
1468
- do {
1469
- fd++;
1470
- } while (!fd_data(fd).protocol && (fd < facil_data->capacity));
1471
-
1472
- if (facil_data->capacity <= fd) {
1473
- facil_data->need_review = 1;
1474
- return;
1475
- }
1476
- reschedule:
1477
- defer(facil_review_timeout, (void *)fd, NULL);
1478
- }
1479
-
1480
- static void perform_idle(void *arg, void *ignr) {
1481
- facil_data->on_idle();
1482
- (void)arg;
1483
- (void)ignr;
1484
- }
1485
-
1486
- /* reactor pattern cycling - common */
1487
- static void facil_cycle_schedule_events(void) {
1488
- static int idle = 0;
1489
- clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
1490
- facil_internal_poll();
1491
- int events;
1492
- if (defer_has_queue()) {
1493
- events = evio_review(0);
1494
- if (events < 0) {
1495
- return;
1496
- }
1497
- if (events > 0) {
1498
- idle = 1;
1499
- }
1500
- } else {
1501
- events = evio_review(EVIO_TICK);
1502
- if (events < 0)
1503
- return;
1504
- if (events > 0) {
1505
- idle = 1;
1506
- } else if (idle) {
1507
- defer(perform_idle, NULL, NULL);
1508
- idle = 0;
1509
- }
1510
- }
1511
- static time_t last_to_review = 0;
1512
- if (facil_data->need_review &&
1513
- facil_data->last_cycle.tv_sec != last_to_review) {
1514
- last_to_review = facil_data->last_cycle.tv_sec;
1515
- facil_data->need_review = 0;
1516
- defer(facil_review_timeout, (void *)0, NULL);
1517
- }
1518
- }
1519
-
1520
- /* reactor pattern cycling during cleanup */
1521
- static void facil_cycle_unwind(void *ignr, void *ignr2) {
1522
- if (facil_data->connection_count) {
1523
- facil_cycle_schedule_events();
1524
- defer(facil_cycle_unwind, ignr, ignr2);
1525
- return;
1526
- }
1527
- pool_pt pool = facil_data->thread_pool;
1528
- facil_data->thread_pool = NULL;
1529
- defer_pool_stop(pool);
1530
- return;
1531
- (void)ignr;
1532
- (void)ignr2;
1533
- }
1534
-
1535
- /* reactor pattern cycling */
1536
- static void facil_cycle(void *ignr, void *ignr2) {
1537
- facil_cycle_schedule_events();
1538
- if (facil_data->active) {
1539
- defer(facil_cycle, ignr, ignr2);
1540
- return;
1541
- }
1542
- /* switch to winding down */
1543
- if (FACIL_PRINT_STATE && !facil_data->spindown) {
1544
- pid_t pid = getpid();
1545
- if (pid != facil_data->parent)
1546
- fprintf(stderr, "* (%d) Detected exit signal.\n", getpid());
1547
- else
1548
- fprintf(stderr, "* Server Detected exit signal.\n");
1549
- }
1550
- pool_pt pool = facil_data->thread_pool;
1551
- facil_data->thread_pool = NULL;
1552
- defer_pool_stop(pool);
1553
- return;
1554
- (void)ignr;
1555
- (void)ignr2;
1556
- }
1557
-
1558
- /**
1559
- OVERRIDE THIS to replace the default `fork` implementation or to inject hooks
1560
- into the forking function.
1561
-
1562
- Behaves like the system's `fork`.
1563
- */
1564
- #pragma weak facil_fork
1565
- int facil_fork(void) { return (int)fork(); }
1566
-
1567
- /** This will be called by child processes, make sure to unlock any existing
1568
- * locks.
1569
- *
1570
- * Known locks:
1571
- * * `defer` tasks lock.
1572
- * * `sock` packet pool lock.
1573
- * * `sock` connection lock (per connection).
1574
- * * `facil` global lock.
1575
- * * `facil` pub/sub lock.
1576
- * * `facil` connection data lock (per connection data).
1577
- * * `facil` protocol lock (per protocol object, placed in `rsv`).
1578
- * * `pubsub` pubsub global lock (should be initialized in facil_external_init.
1579
- * * `pubsub` pubsub client lock (should be initialized in facil_external_init.
1580
- */
1581
- static void facil_worker_startup(uint8_t sentinel) {
1582
- facil_cluster_data.lock = facil_data->global_lock = SPN_LOCK_INIT;
1583
- facil_internal_poll_reset();
1584
- evio_create();
1585
- clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
1586
- facil_external_init();
1587
- if (facil_data->active == 1) {
1588
- /* single process */
1589
- for (int i = 0; i < facil_data->capacity; i++) {
1590
- errno = 0;
1591
- fd_data(i).lock = SPN_LOCK_INIT;
1592
- if (fd_data(i).protocol) {
1593
- fd_data(i).protocol->rsv = 0;
1594
- if (fd_data(i).protocol->service == LISTENER_PROTOCOL_NAME)
1595
- listener_on_start(i);
1596
- else if (fd_data(i).protocol->service == TIMER_PROTOCOL_NAME)
1597
- timer_on_server_start(i);
1598
- else {
1599
- evio_add(i, (void *)sock_fd2uuid(i));
1600
- }
1601
- }
1602
- }
1603
- } else if (sentinel == 0) {
1604
- /* child process */
1605
- for (int i = 0; i < facil_data->capacity; i++) {
1606
- errno = 0;
1607
- fd_data(i).lock = SPN_LOCK_INIT;
1608
- if (fd_data(i).protocol) {
1609
- fd_data(i).protocol->rsv = 0;
1610
- if (fd_data(i).protocol->service == LISTENER_PROTOCOL_NAME)
1611
- listener_on_start(i);
1612
- else if (fd_data(i).protocol->service == TIMER_PROTOCOL_NAME)
1613
- timer_on_server_start(i);
1614
- else {
1615
- /* prevent normal connections from being shared across workers */
1616
- intptr_t uuid = sock_fd2uuid(i);
1617
- if (uuid != -1) {
1618
- sock_force_close(uuid);
1619
- } else {
1620
- sock_on_close(uuid);
1621
- }
1622
- }
1623
- }
1624
- }
1625
- } else {
1626
- /* sentinel process - ignore listening sockets, but keep them open */
1627
- for (int i = 0; i < facil_data->capacity; i++) {
1628
- fd_data(i).lock = SPN_LOCK_INIT;
1629
- if (fd_data(i).protocol) {
1630
- fd_data(i).protocol->rsv = 0;
1631
- if (fd_data(i).protocol->service == TIMER_PROTOCOL_NAME)
1632
- timer_on_server_start(i);
1633
- else if (fd_data(i).protocol->service != LISTENER_PROTOCOL_NAME) {
1634
- evio_add(i, (void *)sock_fd2uuid(i));
1635
- }
1636
- }
1637
- }
1638
- }
1639
- /* Clear all existing events & flush `facil_cycle` out. */
1640
- {
1641
- facil_data->spindown = 1;
1642
- uint16_t old_active = facil_data->active;
1643
- facil_data->active = 0;
1644
- defer_perform();
1645
- facil_data->active = old_active;
1646
- facil_data->spindown = 0;
1647
- }
1648
- /* called after connection cleanup, as it should open connections. */
1649
- if (cluster_on_start()) {
1650
- facil_data->thread_pool = NULL;
1651
- return;
1652
- }
1653
- /* call any external startup callbacks. */
1654
- facil_external_init2();
1655
- /* add cycling to the defer queue to setup the reactor pattern. */
1656
- facil_data->need_review = 1;
1657
- defer(facil_cycle, NULL, NULL);
1658
-
1659
- if (FACIL_PRINT_STATE) {
1660
- if (sentinel || facil_data->parent == getpid()) {
1661
- fprintf(stderr,
1662
- "Server is running %u %s X %u %s, press ^C to stop\n"
1663
- "* Detected capacity: %zd open file limit\n"
1664
- "* Root pid: %d\n",
1665
- facil_data->active, facil_data->active > 1 ? "workers" : "worker",
1666
- facil_data->threads,
1667
- facil_data->threads > 1 ? "threads" : "thread",
1668
- facil_data->capacity, facil_data->parent);
1669
- } else {
1670
- defer(print_pid, NULL, NULL);
1671
- }
1672
- }
1673
- facil_data->thread_pool =
1674
- defer_pool_start((sentinel ? 1 : facil_data->threads));
1675
- if (facil_data->thread_pool)
1676
- defer_pool_wait(facil_data->thread_pool);
1677
- }
1678
-
1679
- static void facil_worker_cleanup(void) {
1680
- facil_data->active = 0;
1681
- facil_cluster_signal_children();
1682
- for (int i = 0; i <= facil_data->capacity; ++i) {
1683
- intptr_t uuid;
1684
- if (is_counted_protocol(fd_data(i).protocol) &&
1685
- (uuid = sock_fd2uuid(i)) >= 0) {
1686
- defer(deferred_on_shutdown, (void *)uuid, NULL);
1687
- }
1688
- }
1689
- facil_data->thread_pool = defer_pool_start(facil_data->threads);
1690
- if (facil_data->thread_pool) {
1691
- defer(facil_cycle_unwind, NULL, NULL);
1692
- defer_pool_wait(facil_data->thread_pool);
1693
- facil_data->thread_pool = NULL;
1694
- }
1695
- fprintf(stderr, "* %d cleaning up.\n", getpid());
1696
- /* close leftovers */
1697
- for (int i = 0; i <= facil_data->capacity; ++i) {
1698
- intptr_t uuid;
1699
- if (fd_data(i).protocol && (uuid = sock_fd2uuid(i)) >= 0) {
1700
- sock_force_close(uuid);
1701
- }
1702
- }
1703
- defer_perform();
1704
- if (facil_data->on_finish) {
1705
- facil_data->on_finish();
1706
- }
1707
- defer_perform();
1708
- evio_close();
1709
- facil_external_cleanup();
1710
-
1711
- if (facil_data->parent == getpid()) {
1712
- kill(0, SIGINT);
1713
- while (wait(NULL) != -1)
1714
- ;
1715
- if (FACIL_PRINT_STATE) {
1716
- fprintf(stderr, "\n --- Shutdown Complete ---\n");
1717
- }
1718
- }
1719
- }
1720
-
1721
- static void facil_sentinel_task(void *arg1, void *arg2);
1722
- static void *facil_sentinel_worker_thread(void *arg) {
1723
- errno = 0;
1724
- pid_t child = facil_fork();
1725
- if (arg && child) {
1726
- spn_unlock((spn_lock_i *)arg);
1727
- }
1728
- if (child == -1) {
1729
- perror("FATAL ERROR: couldn't spawn worker.");
1730
- kill(facil_parent_pid(), SIGINT);
1731
- facil_stop();
1732
- return NULL;
1733
- } else if (child) {
1734
- int status;
1735
- waitpid(child, &status, 0);
1736
- #if DEBUG
1737
- if (facil_data->active) { /* !WIFEXITED(status) || WEXITSTATUS(status) */
1738
- if (!WIFEXITED(status) || WEXITSTATUS(status)) {
1739
- fprintf(stderr,
1740
- "FATAL ERROR: Child worker (%d) crashed. Stopping services in "
1741
- "DEBUG mode.\n",
1742
- child);
1743
- } else {
1744
- fprintf(
1745
- stderr,
1746
- "INFO (FATAL): Child worker (%d) shutdown. Stopping services in "
1747
- "DEBUG mode.\n",
1748
- child);
1749
- }
1750
- kill(0, SIGINT);
1751
- }
1752
- #else
1753
- if (facil_data->active) {
1754
- if (!WIFEXITED(status) || WEXITSTATUS(status)) {
1755
- fprintf(stderr,
1756
- "ERROR: Child worker (%d) crashed. Respawning worker.\n",
1757
- child);
1758
- } else {
1759
- fprintf(stderr,
1760
- "INFO: Child worker (%d) shutdown. Respawning worker.\n",
1761
- child);
1762
- }
1763
- defer(facil_sentinel_task, NULL, NULL);
1764
- }
1765
- #endif
1766
- } else {
1767
- facil_worker_startup(0);
1768
- facil_worker_cleanup();
1769
- exit(0);
1770
- }
1771
- return NULL;
1772
- }
1773
-
1774
- #if FIO_SENTINEL_USE_PTHREAD
1775
- static void facil_sentinel_task(void *arg1, void *arg2) {
1776
- if (!facil_data->active)
1777
- return;
1778
- spn_lock_i pre_fork_lock = SPN_LOCK_INIT;
1779
- spn_lock(&pre_fork_lock);
1780
- pthread_t sentinel;
1781
- if (pthread_create(&sentinel, NULL, facil_sentinel_worker_thread,
1782
- (void *)&pre_fork_lock)) {
1783
- perror("FATAL ERROR: couldn't start sentinel thread");
1784
- exit(errno);
1785
- }
1786
- pthread_detach(sentinel);
1787
- spn_lock(&pre_fork_lock); /* will wait for worker thread to release lock. */
1788
- facil_cluster_data.listening.on_data(facil_cluster_data.root,
1789
- &facil_cluster_data.listening);
1790
- (void)arg1;
1791
- (void)arg2;
1792
- }
1793
- #else
1794
- static void facil_sentinel_task(void *arg1, void *arg2) {
1795
- if (!facil_data->active)
1796
- return;
1797
- spn_lock_i pre_fork_lock = SPN_LOCK_INIT;
1798
- spn_lock(&pre_fork_lock);
1799
- void *thrd =
1800
- defer_new_thread(facil_sentinel_worker_thread, (void *)&pre_fork_lock);
1801
- defer_free_thread(thrd);
1802
- spn_lock(&pre_fork_lock); /* will wait for worker thread to release lock. */
1803
- facil_cluster_data.listening.on_data(facil_cluster_data.root,
1804
- &facil_cluster_data.listening);
1805
- (void)arg1;
1806
- (void)arg2;
1807
- }
1808
- #endif
1809
-
1810
- /* handles the SIGUSR1, SIGINT and SIGTERM signals. */
1811
- static void sig_int_handler(int sig) {
1812
- switch (sig) {
1813
- #if !FACIL_DISABLE_HOT_RESTART
1814
- case SIGUSR1:
1815
- facil_signal_children_flag = 1;
1816
- break;
1817
- #endif
1818
- case SIGINT: /* fallthrough */
1819
- case SIGTERM: /* fallthrough */
1820
- facil_stop();
1821
- break;
1822
- default:
1823
- break;
1824
- }
1825
- }
1826
-
1827
- /* setup handling for the SIGUSR1, SIGPIPE, SIGINT and SIGTERM signals. */
1828
- static void facil_setup_signal_handler(void) {
1829
- /* setup signal handling */
1830
- struct sigaction act, old;
1831
-
1832
- act.sa_handler = sig_int_handler;
1833
- sigemptyset(&act.sa_mask);
1834
- act.sa_flags = SA_RESTART | SA_NOCLDSTOP;
1835
-
1836
- if (sigaction(SIGINT, &act, &old)) {
1837
- perror("couldn't set signal handler");
1838
- return;
1839
- };
1840
-
1841
- if (sigaction(SIGTERM, &act, &old)) {
1842
- perror("couldn't set signal handler");
1843
- return;
1844
- };
1845
- #if !FACIL_DISABLE_HOT_RESTART
1846
- if (sigaction(SIGUSR1, &act, &old)) {
1847
- perror("couldn't set signal handler");
1848
- return;
1849
- };
1850
- #endif
1851
-
1852
- act.sa_handler = SIG_IGN;
1853
- if (sigaction(SIGPIPE, &act, &old)) {
1854
- perror("couldn't set signal handler");
1855
- return;
1856
- };
1857
- }
1858
-
1859
- /*
1860
- * Zombie Reaping
1861
- * With thanks to Dr Graham D Shaw.
1862
- * http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html
1863
- */
1864
- static void reap_child_handler(int sig) {
1865
- (void)(sig);
1866
- int old_errno = errno;
1867
- while (waitpid(-1, NULL, WNOHANG) > 0)
1868
- ;
1869
- errno = old_errno;
1870
- }
1871
-
1872
- /* initializes zombie reaping for the process */
1873
- void facil_reap_children(void) {
1874
- struct sigaction sa;
1875
- sa.sa_handler = reap_child_handler;
1876
- sigemptyset(&sa.sa_mask);
1877
- sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
1878
- if (sigaction(SIGCHLD, &sa, 0) == -1) {
1879
- perror("Child reaping initialization failed");
1880
- kill(0, SIGINT);
1881
- exit(errno);
1882
- }
1883
- }
1884
-
1885
- /** returns facil.io's parent (root) process pid. */
1886
- pid_t facil_parent_pid(void) {
1887
- if (!facil_data)
1888
- facil_lib_init();
1889
- return facil_data->parent;
1890
- }
1891
-
1892
- static inline size_t facil_detect_cpu_cores(void) {
1893
- ssize_t cpu_count = 0;
1894
- #ifdef _SC_NPROCESSORS_ONLN
1895
- cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
1896
- if (cpu_count < 0) {
1897
- if (FACIL_PRINT_STATE) {
1898
- fprintf(stderr, "WARNING: CPU core count auto-detection failed.\n");
1899
- }
1900
- return 0;
1901
- }
1902
- #else
1903
- if (1 || FACIL_PRINT_STATE) {
1904
- fprintf(stderr, "WARNING: CPU core count auto-detection failed.\n");
1905
- }
1906
- #endif
1907
- return cpu_count;
1908
- }
1909
-
1910
- /**
1911
- * Returns the number of expected threads / processes to be used by facil.io.
1912
- *
1913
- * The pointers should start with valid values that match the expected threads /
1914
- * processes values passed to `facil_run`.
1915
- *
1916
- * The data in the pointers will be overwritten with the result.
1917
- */
1918
- void facil_expected_concurrency(int16_t *threads, int16_t *processes) {
1919
- if (!threads || !processes)
1920
- return;
1921
- if (!*threads && !*processes) {
1922
- /* both options set to 0 - default to cores*cores matrix */
1923
- ssize_t cpu_count = facil_detect_cpu_cores();
1924
- #if FACIL_CPU_CORES_LIMIT
1925
- if (cpu_count > FACIL_CPU_CORES_LIMIT) {
1926
- static int print_cores_warning = 1;
1927
- if (print_cores_warning) {
1928
- fprintf(
1929
- stderr,
1930
- "INFO: Detected %zu cores. Capping auto-detection of cores "
1931
- "to %zu.\n"
1932
- " Avoid this message by setting threads / workers manually.\n"
1933
- " To increase auto-detection limit, recompile with:\n"
1934
- " -DFACIL_CPU_CORES_LIMIT=%zu \n",
1935
- (size_t)cpu_count, (size_t)FACIL_CPU_CORES_LIMIT,
1936
- (size_t)cpu_count);
1937
- print_cores_warning = 0;
1938
- }
1939
- cpu_count = FACIL_CPU_CORES_LIMIT;
1940
- }
1941
- #endif
1942
- *threads = *processes = (int16_t)cpu_count;
1943
- } else if (*threads < 0 || *processes < 0) {
1944
- /* Set any option that is less than 0 be equal to cores/value */
1945
- /* Set any option equal to 0 be equal to the other option in value */
1946
- ssize_t cpu_count = facil_detect_cpu_cores();
1947
-
1948
- if (cpu_count > 0) {
1949
- int16_t tmp_threads = 0;
1950
- if (*threads < 0)
1951
- tmp_threads = (int16_t)(cpu_count / (*threads * -1));
1952
- else if (*threads == 0)
1953
- tmp_threads = -1 * *processes;
1954
- if (*processes < 0)
1955
- *processes = (int16_t)(cpu_count / (*processes * -1));
1956
- else if (*processes == 0)
1957
- *processes = -1 * *threads;
1958
- *threads = tmp_threads;
1959
- }
1960
- }
1961
-
1962
- /* make sure we have at least one process and at least one thread */
1963
- if (*processes <= 0)
1964
- *processes = 1;
1965
- if (*threads <= 0)
1966
- *threads = 1;
1967
- }
1968
-
1969
- #undef facil_run
1970
- void facil_run(struct facil_run_args args) {
1971
- signal(SIGPIPE, SIG_IGN);
1972
- if (!facil_data)
1973
- facil_lib_init();
1974
- if (!args.on_idle)
1975
- args.on_idle = mock_idle;
1976
- if (!args.on_finish)
1977
- args.on_finish = mock_idle;
1978
-
1979
- /* compute actual concurrency */
1980
- facil_expected_concurrency(&args.threads, &args.processes);
1981
-
1982
- /* listen to SIGINT / SIGTERM */
1983
- facil_setup_signal_handler();
1984
-
1985
- /* activate facil, fork if needed */
1986
- facil_data->active = (uint16_t)args.processes;
1987
- facil_data->threads = (uint16_t)args.threads;
1988
- facil_data->on_finish = args.on_finish;
1989
- facil_data->on_idle = args.on_idle;
1990
- /* initialize cluster */
1991
- if (args.processes > 1) {
1992
- if (facil_cluster_init()) {
1993
- kill(0, SIGINT);
1994
- if (FACIL_PRINT_STATE) {
1995
- fprintf(stderr, "\n !!! Crashed trying to "
1996
- "start the service !!!\n");
1997
- }
1998
- exit(-1);
1999
- }
2000
- for (int i = 0; i < args.processes && facil_data->active; ++i) {
2001
- facil_sentinel_task(NULL, NULL);
2002
- }
2003
- facil_worker_startup(1);
2004
- } else {
2005
- facil_worker_startup(0);
2006
- }
2007
- facil_worker_cleanup();
2008
- }
2009
-
2010
- /**
2011
- * returns true (1) if the facil.io engine is already running.
2012
- */
2013
- int facil_is_running(void) { return (facil_data && facil_data->active > 0); }
2014
-
2015
- /* *****************************************************************************
2016
- Setting the protocol
2017
- ***************************************************************************** */
2018
-
2019
- /* managing the protocol pointer array and the `on_close` callback */
2020
- static int facil_attach_state(intptr_t uuid, protocol_s *protocol,
2021
- protocol_metadata_s state) {
2022
- if (!facil_data)
2023
- facil_lib_init();
2024
- if (protocol) {
2025
- if (!protocol->on_close) {
2026
- protocol->on_close = mock_on_close;
2027
- }
2028
- if (!protocol->on_data) {
2029
- protocol->on_data = mock_on_data;
2030
- }
2031
- if (!protocol->on_ready) {
2032
- protocol->on_ready = mock_on_ev;
2033
- }
2034
- if (!protocol->ping) {
2035
- protocol->ping = mock_ping;
2036
- }
2037
- if (!protocol->on_shutdown) {
2038
- protocol->on_shutdown = mock_on_ev;
2039
- }
2040
- prt_meta(protocol) = state;
2041
- }
2042
- spn_lock(&uuid_data(uuid).lock);
2043
- if (!sock_isvalid(uuid)) {
2044
- spn_unlock(&uuid_data(uuid).lock);
2045
- if (protocol)
2046
- defer(deferred_on_close, (void *)uuid, protocol);
2047
- if (uuid == -1)
2048
- errno = EBADF;
2049
- else
2050
- errno = ENOTCONN;
2051
- return -1;
2052
- } else {
2053
- if (is_counted_protocol(protocol)) {
2054
- spn_add(&facil_data->connection_count, 1);
2055
- }
2056
- }
2057
- protocol_s *old_protocol = uuid_data(uuid).protocol;
2058
- uuid_data(uuid).protocol = protocol;
2059
- uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
2060
- spn_unlock(&uuid_data(uuid).lock);
2061
- if (old_protocol) {
2062
- if (is_counted_protocol(old_protocol)) {
2063
- spn_sub(&facil_data->connection_count, 1);
2064
- }
2065
- defer(deferred_on_close, (void *)uuid, old_protocol);
2066
- } else if (evio_isactive() && protocol) {
2067
- return evio_add(sock_uuid2fd(uuid), (void *)uuid);
2068
- }
2069
- return 0;
2070
- }
2071
-
2072
- /** Attaches (or updates) a protocol object to a socket UUID.
2073
- * Returns -1 on error and 0 on success.
2074
- */
2075
- int facil_attach(intptr_t uuid, protocol_s *protocol) {
2076
- return facil_attach_state(uuid, protocol, (protocol_metadata_s){.rsv = 0});
2077
- }
2078
-
2079
- /**
2080
- * Attaches (or updates) a LOCKED protocol object to a socket UUID.
2081
- */
2082
- int facil_attach_locked(intptr_t uuid, protocol_s *protocol) {
2083
- {
2084
- protocol_metadata_s state = {.rsv = 0};
2085
- spn_lock(state.locks + FIO_PR_LOCK_TASK);
2086
- return facil_attach_state(uuid, protocol, state);
2087
- }
2088
- }
2089
-
2090
- /** Sets a timeout for a specific connection (if active). */
2091
- void facil_set_timeout(intptr_t uuid, uint8_t timeout) {
2092
- if (sock_isvalid(uuid)) {
2093
- uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
2094
- uuid_data(uuid).timeout = timeout;
2095
- }
2096
- }
2097
- /** Gets a timeout for a specific connection. Returns 0 if there's no set
2098
- * timeout or the connection is inactive. */
2099
- uint8_t facil_get_timeout(intptr_t uuid) { return uuid_data(uuid).timeout; }
2100
-
2101
- /* *****************************************************************************
2102
- Misc helpers
2103
- ***************************************************************************** */
2104
-
2105
- /**
2106
- Returns the last time the server reviewed any pending IO events.
2107
- */
2108
- struct timespec facil_last_tick(void) {
2109
- if (!facil_data) {
2110
- facil_lib_init();
2111
- clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
2112
- }
2113
- return facil_data->last_cycle;
2114
- }
2115
-
2116
- /**
2117
- * This function allows out-of-task access to a connection's `protocol_s`
2118
- * object by attempting to lock it.
2119
- */
2120
- protocol_s *facil_protocol_try_lock(intptr_t uuid,
2121
- enum facil_protocol_lock_e type) {
2122
- if (!sock_isvalid(uuid) || !uuid_data(uuid).protocol) {
2123
- errno = EBADF;
2124
- return NULL;
2125
- }
2126
- return protocol_try_lock(sock_uuid2fd(uuid), type);
2127
- }
2128
- /** See `facil_protocol_try_lock` for details. */
2129
- void facil_protocol_unlock(protocol_s *pr, enum facil_protocol_lock_e type) {
2130
- if (!pr)
2131
- return;
2132
- protocol_unlock(pr, type);
2133
- }
2134
- /** Counts all the connections of a specific type. */
2135
- size_t facil_count(void *service) {
2136
- size_t count = 0;
2137
- for (intptr_t i = 0; i < facil_data->capacity; i++) {
2138
- void *tmp = NULL;
2139
- spn_lock(&fd_data(i).lock);
2140
- if (fd_data(i).protocol && fd_data(i).protocol->service)
2141
- tmp = (void *)fd_data(i).protocol->service;
2142
- spn_unlock(&fd_data(i).lock);
2143
- if (tmp != LISTENER_PROTOCOL_NAME && tmp != TIMER_PROTOCOL_NAME &&
2144
- (!service || (tmp == service)))
2145
- count++;
2146
- }
2147
- return count;
2148
- }
2149
-
2150
- /* *****************************************************************************
2151
- Task Management - `facil_defer`, `facil_each`
2152
- ***************************************************************************** */
2153
-
2154
- struct task {
2155
- intptr_t origin;
2156
- void (*func)(intptr_t uuid, protocol_s *, void *arg);
2157
- void *arg;
2158
- void (*on_done)(intptr_t uuid, void *arg);
2159
- const void *service;
2160
- uint32_t count;
2161
- enum facil_protocol_lock_e task_type;
2162
- spn_lock_i lock;
2163
- };
2164
-
2165
- static inline struct task *alloc_facil_task(void) {
2166
- return fio_malloc(sizeof(struct task));
2167
- }
2168
-
2169
- static inline void free_facil_task(struct task *task) { fio_free(task); }
2170
-
2171
- static void mock_on_task_done(intptr_t uuid, void *arg) {
2172
- (void)uuid;
2173
- (void)arg;
2174
- }
2175
-
2176
- static void perform_single_task(void *v_uuid, void *v_task) {
2177
- struct task *task = v_task;
2178
- if (!uuid_data(v_uuid).protocol)
2179
- goto fallback;
2180
- protocol_s *pr = protocol_try_lock(sock_uuid2fd(v_uuid), task->task_type);
2181
- if (!pr)
2182
- goto defer;
2183
- if (pr->service == CONNECTOR_PROTOCOL_NAME) {
2184
- protocol_unlock(pr, task->task_type);
2185
- goto defer;
2186
- }
2187
- task->func((intptr_t)v_uuid, pr, task->arg);
2188
- protocol_unlock(pr, task->task_type);
2189
- free_facil_task(task);
2190
- return;
2191
- fallback:
2192
- task->on_done((intptr_t)v_uuid, task->arg);
2193
- free_facil_task(task);
2194
- return;
2195
- defer:
2196
- defer(perform_single_task, v_uuid, v_task);
2197
- return;
2198
- }
2199
-
2200
- static void finish_multi_task(void *v_fd, void *v_task) {
2201
- struct task *task = v_task;
2202
- if (spn_trylock(&task->lock))
2203
- goto reschedule;
2204
- task->count--;
2205
- if (task->count) {
2206
- spn_unlock(&task->lock);
2207
- return;
2208
- }
2209
- task->on_done(task->origin, task->arg);
2210
- free_facil_task(task);
2211
- return;
2212
- reschedule:
2213
- defer(finish_multi_task, v_fd, v_task);
2214
- }
2215
-
2216
- static void perform_multi_task(void *v_fd, void *v_task) {
2217
- if (!fd_data((intptr_t)v_fd).protocol) {
2218
- finish_multi_task(v_fd, v_task);
2219
- return;
2220
- }
2221
- struct task *task = v_task;
2222
- protocol_s *pr = protocol_try_lock((intptr_t)v_fd, task->task_type);
2223
- if (!pr)
2224
- goto reschedule;
2225
- if (pr->service == task->service) {
2226
- const intptr_t uuid = sock_fd2uuid((int)(intptr_t)v_fd);
2227
- task->func(uuid, pr, task->arg);
2228
- }
2229
- protocol_unlock(pr, task->task_type);
2230
- defer(finish_multi_task, v_fd, v_task);
2231
- return;
2232
- reschedule:
2233
- // fprintf(stderr, "rescheduling multi for %p\n", v_fd);
2234
- defer(perform_multi_task, v_fd, v_task);
2235
- }
2236
-
2237
- static void schedule_multi_task(void *v_fd, void *v_task) {
2238
- struct task *task = v_task;
2239
- intptr_t fd = (intptr_t)v_fd;
2240
- for (size_t i = 0; i < 64; i++) {
2241
- if (!fd_data(fd).protocol)
2242
- goto finish;
2243
- if (spn_trylock(&fd_data(fd).lock))
2244
- goto reschedule;
2245
- if (!fd_data(fd).protocol ||
2246
- fd_data(fd).protocol->service != task->service || fd == task->origin) {
2247
- spn_unlock(&fd_data(fd).lock);
2248
- goto finish;
2249
- }
2250
- spn_unlock(&fd_data(fd).lock);
2251
- spn_lock(&task->lock);
2252
- task->count++;
2253
- spn_unlock(&task->lock);
2254
- defer(perform_multi_task, (void *)fd, task);
2255
- finish:
2256
- do {
2257
- fd++;
2258
- } while (!fd_data(fd).protocol && (fd < facil_data->capacity));
2259
- if (fd >= (intptr_t)facil_data->capacity)
2260
- goto complete;
2261
- }
2262
- reschedule:
2263
- schedule_multi_task((void *)fd, v_task);
2264
- return;
2265
- complete:
2266
- defer(finish_multi_task, NULL, v_task);
2267
- }
2268
- /**
2269
- * Schedules a protected connection task. The task will run within the
2270
- * connection's lock.
2271
- *
2272
- * If the connection is closed before the task can run, the
2273
- * `fallback` task wil be called instead, allowing for resource cleanup.
2274
- */
2275
- #undef facil_defer
2276
- void facil_defer(struct facil_defer_args_s args) {
2277
- if (!args.fallback)
2278
- args.fallback = mock_on_task_done;
2279
- if (!args.type)
2280
- args.type = FIO_PR_LOCK_TASK;
2281
- if (!args.task || !uuid_data(args.uuid).protocol || args.uuid < 0 ||
2282
- !sock_isvalid(args.uuid))
2283
- goto error;
2284
- struct task *task = alloc_facil_task();
2285
- if (!task)
2286
- goto error;
2287
- *task = (struct task){
2288
- .func = args.task, .arg = args.arg, .on_done = args.fallback};
2289
- defer(perform_single_task, (void *)args.uuid, task);
2290
- return;
2291
- error:
2292
- defer((void (*)(void *, void *))args.fallback, (void *)args.uuid, args.arg);
2293
- }
2294
-
2295
- /**
2296
- * Schedules a protected connection task for each `service` connection.
2297
- * The tasks will run within each of the connection's locks.
2298
- *
2299
- * Once all the tasks were performed, the `on_complete` callback will be called.
2300
- */
2301
- #undef facil_each
2302
- int facil_each(struct facil_each_args_s args) {
2303
- if (!args.on_complete)
2304
- args.on_complete = mock_on_task_done;
2305
- if (!args.task_type)
2306
- args.task_type = FIO_PR_LOCK_TASK;
2307
- if (!args.task)
2308
- goto error;
2309
- struct task *task = alloc_facil_task();
2310
- if (!task)
2311
- goto error;
2312
- *task = (struct task){.origin = args.origin,
2313
- .func = args.task,
2314
- .arg = args.arg,
2315
- .on_done = args.on_complete,
2316
- .service = args.service,
2317
- .task_type = args.task_type,
2318
- .count = 1};
2319
- defer(schedule_multi_task, (void *)0, task);
2320
- return 0;
2321
- error:
2322
- defer((void (*)(void *, void *))args.on_complete, (void *)args.origin,
2323
- args.arg);
2324
- return -1;
2325
- }