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