iodine 0.2.17 → 0.3.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -3
  4. data/bin/config.ru +23 -2
  5. data/bin/http-hello +1 -1
  6. data/bin/ws-shootout +5 -0
  7. data/ext/iodine/defer.c +468 -0
  8. data/ext/iodine/defer.h +105 -0
  9. data/ext/iodine/evio.c +263 -0
  10. data/ext/iodine/evio.h +133 -0
  11. data/ext/iodine/extconf.rb +2 -1
  12. data/ext/iodine/facil.c +958 -0
  13. data/ext/iodine/facil.h +423 -0
  14. data/ext/iodine/http.c +90 -0
  15. data/ext/iodine/http.h +50 -12
  16. data/ext/iodine/http1.c +200 -267
  17. data/ext/iodine/http1.h +17 -26
  18. data/ext/iodine/http1_request.c +81 -0
  19. data/ext/iodine/http1_request.h +58 -0
  20. data/ext/iodine/http1_response.c +403 -0
  21. data/ext/iodine/http1_response.h +90 -0
  22. data/ext/iodine/http1_simple_parser.c +124 -108
  23. data/ext/iodine/http1_simple_parser.h +8 -3
  24. data/ext/iodine/http_request.c +104 -0
  25. data/ext/iodine/http_request.h +58 -102
  26. data/ext/iodine/http_response.c +212 -208
  27. data/ext/iodine/http_response.h +89 -252
  28. data/ext/iodine/iodine_core.c +57 -46
  29. data/ext/iodine/iodine_core.h +3 -1
  30. data/ext/iodine/iodine_http.c +105 -81
  31. data/ext/iodine/iodine_websocket.c +17 -13
  32. data/ext/iodine/iodine_websocket.h +1 -0
  33. data/ext/iodine/rb-call.c +9 -7
  34. data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
  35. data/ext/iodine/rb-rack-io.c +12 -6
  36. data/ext/iodine/rb-rack-io.h +1 -1
  37. data/ext/iodine/rb-registry.c +5 -2
  38. data/ext/iodine/sock.c +1159 -0
  39. data/ext/iodine/{libsock.h → sock.h} +138 -142
  40. data/ext/iodine/spnlock.inc +77 -0
  41. data/ext/iodine/websockets.c +101 -112
  42. data/ext/iodine/websockets.h +38 -19
  43. data/iodine.gemspec +3 -3
  44. data/lib/iodine/version.rb +1 -1
  45. data/lib/rack/handler/iodine.rb +6 -6
  46. metadata +23 -19
  47. data/ext/iodine/http_response_http1.h +0 -382
  48. data/ext/iodine/libasync.c +0 -570
  49. data/ext/iodine/libasync.h +0 -122
  50. data/ext/iodine/libreact.c +0 -350
  51. data/ext/iodine/libreact.h +0 -244
  52. data/ext/iodine/libserver.c +0 -957
  53. data/ext/iodine/libserver.h +0 -481
  54. data/ext/iodine/libsock.c +0 -1025
  55. data/ext/iodine/spnlock.h +0 -243
@@ -29,7 +29,8 @@ elsif find_executable('gcc-4.9')
29
29
  $CPP = ENV['CPP'] = find_executable('g++-4.9') ? 'g++-4.9' : 'gcc-4.9'
30
30
  puts 'using gcc-4.9 compiler.'
31
31
  else
32
- check_for_stdatomics
32
+ # check_for_stdatomics
33
+ puts 'using an unknown (old?) compiler... who knows if this will work out... we hope.'
33
34
  end
34
35
 
35
36
  $CFLAGS = '-std=c11 -O3 -Wall -DSERVER_DELAY_IO=1'
@@ -0,0 +1,958 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2016-2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include "spnlock.inc"
8
+
9
+ #include "evio.h"
10
+ #include "facil.h"
11
+
12
+ #include <errno.h>
13
+ #include <signal.h>
14
+ #include <stdio.h>
15
+ #include <stdlib.h>
16
+ #include <string.h>
17
+ #include <sys/mman.h>
18
+
19
+ /* *****************************************************************************
20
+ Data Structures
21
+ ***************************************************************************** */
22
+ typedef struct ProtocolMetadata {
23
+ spn_lock_i locks[3];
24
+ unsigned rsv : 8;
25
+ } protocol_metadata_s;
26
+
27
+ #define prt_meta(prt) (*((protocol_metadata_s *)(&(prt)->rsv)))
28
+
29
+ struct connection_data_s {
30
+ protocol_s *protocol;
31
+ time_t active;
32
+ uint8_t timeout;
33
+ spn_lock_i lock;
34
+ };
35
+
36
+ static struct facil_data_s {
37
+ spn_lock_i global_lock;
38
+ uint8_t need_review;
39
+ ssize_t capacity;
40
+ time_t last_cycle;
41
+ pid_t parent;
42
+ struct connection_data_s conn[];
43
+ } * facil_data;
44
+
45
+ #define fd_data(fd) (facil_data->conn[(fd)])
46
+ #define uuid_data(uuid) fd_data(sock_uuid2fd((uuid)))
47
+ #define uuid_prt_meta(uuid) prt_meta(uuid_data((uuid)).protocol)
48
+
49
+ static inline void clear_connection_data_unsafe(intptr_t uuid,
50
+ protocol_s *protocol) {
51
+ uuid_data(uuid) = (struct connection_data_s){.active = facil_data->last_cycle,
52
+ .protocol = protocol,
53
+ .lock = uuid_data(uuid).lock};
54
+ }
55
+ /** locks a connection's protocol returns a pointer that need to be unlocked. */
56
+ inline static protocol_s *protocol_try_lock(intptr_t fd,
57
+ enum facil_protocol_lock_e type) {
58
+ if (spn_trylock(&fd_data(fd).lock))
59
+ return NULL;
60
+ protocol_s *pr = fd_data(fd).protocol;
61
+ if (!pr) {
62
+ spn_unlock(&fd_data(fd).lock);
63
+ return NULL;
64
+ }
65
+ if (spn_trylock(&prt_meta(pr).locks[type]))
66
+ pr = NULL;
67
+ spn_unlock(&fd_data(fd).lock);
68
+ return pr;
69
+ }
70
+ /** See `facil_protocol_try_lock` for details. */
71
+ inline static void protocol_unlock(protocol_s *pr,
72
+ enum facil_protocol_lock_e type) {
73
+ spn_unlock(&prt_meta(pr).locks[type]);
74
+ }
75
+
76
+ /* *****************************************************************************
77
+ Deferred event handlers
78
+ ***************************************************************************** */
79
+ static void deferred_on_close(void *arg, void *arg2) {
80
+ protocol_s *pr = arg;
81
+ if (pr->rsv)
82
+ goto postpone;
83
+ pr->on_close(pr);
84
+ return;
85
+ postpone:
86
+ defer(deferred_on_close, arg, NULL);
87
+ (void)arg2;
88
+ }
89
+
90
+ static void deferred_on_ready(void *arg, void *arg2) {
91
+ if (!uuid_data(arg).protocol)
92
+ return;
93
+ protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
94
+ if (!pr)
95
+ goto postpone;
96
+ pr->on_ready((intptr_t)arg, pr);
97
+ protocol_unlock(pr, FIO_PR_LOCK_WRITE);
98
+ return;
99
+ postpone:
100
+ defer(deferred_on_ready, arg, NULL);
101
+ (void)arg2;
102
+ }
103
+
104
+ static void deferred_on_shutdown(void *arg, void *arg2) {
105
+ if (!uuid_data(arg).protocol)
106
+ return;
107
+ protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
108
+ if (!pr)
109
+ goto postpone;
110
+ pr->on_shutdown((intptr_t)arg, pr);
111
+ protocol_unlock(pr, FIO_PR_LOCK_WRITE);
112
+ sock_close((intptr_t)arg);
113
+ return;
114
+ postpone:
115
+ defer(deferred_on_shutdown, arg, NULL);
116
+ (void)arg2;
117
+ }
118
+
119
+ static void deferred_on_data(void *arg, void *arg2) {
120
+ if (!uuid_data(arg).protocol)
121
+ return;
122
+ protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_TASK);
123
+ if (!pr)
124
+ goto postpone;
125
+ pr->on_data((intptr_t)arg, pr);
126
+ protocol_unlock(pr, FIO_PR_LOCK_TASK);
127
+ return;
128
+ postpone:
129
+ defer(deferred_on_data, arg, NULL);
130
+ (void)arg2;
131
+ }
132
+
133
+ static void deferred_ping(void *arg, void *arg2) {
134
+ if (!uuid_data(arg).protocol ||
135
+ (uuid_data(arg).timeout &&
136
+ (uuid_data(arg).timeout >
137
+ (facil_data->last_cycle - uuid_data(arg).active)))) {
138
+ return;
139
+ }
140
+ protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
141
+ if (!pr)
142
+ goto postpone;
143
+ pr->ping((intptr_t)arg, pr);
144
+ protocol_unlock(pr, FIO_PR_LOCK_WRITE);
145
+ return;
146
+ postpone:
147
+ defer(deferred_ping, arg, NULL);
148
+ (void)arg2;
149
+ }
150
+
151
+ /* *****************************************************************************
152
+ Event Handlers (evio)
153
+ ***************************************************************************** */
154
+ static void sock_flush_defer(void *arg, void *ignored) {
155
+ (void)ignored;
156
+ sock_flush((intptr_t)arg);
157
+ }
158
+
159
+ void evio_on_ready(void *arg) {
160
+ defer(sock_flush_defer, arg, NULL);
161
+ defer(deferred_on_ready, arg, NULL);
162
+ }
163
+ void evio_on_close(void *arg) { sock_force_close((intptr_t)arg); }
164
+ void evio_on_error(void *arg) { sock_force_close((intptr_t)arg); }
165
+ void evio_on_data(void *arg) { defer(deferred_on_data, arg, NULL); }
166
+
167
+ /* *****************************************************************************
168
+ Socket callbacks
169
+ ***************************************************************************** */
170
+
171
+ void sock_on_close(intptr_t uuid) {
172
+ spn_lock(&uuid_data(uuid).lock);
173
+ protocol_s *old_protocol = uuid_data(uuid).protocol;
174
+ clear_connection_data_unsafe(uuid, NULL);
175
+ spn_unlock(&uuid_data(uuid).lock);
176
+ if (old_protocol)
177
+ defer(deferred_on_close, old_protocol, NULL);
178
+ }
179
+
180
+ void sock_touch(intptr_t uuid) {
181
+ uuid_data(uuid).active = facil_data->last_cycle;
182
+ }
183
+
184
+ /* *****************************************************************************
185
+ Initialization and Cleanup
186
+ ***************************************************************************** */
187
+ static spn_lock_i facil_libinit_lock = SPN_LOCK_INIT;
188
+
189
+ static void facil_libcleanup(void) {
190
+ /* free memory */
191
+ spn_lock(&facil_libinit_lock);
192
+ if (facil_data) {
193
+ munmap(facil_data,
194
+ sizeof(*facil_data) +
195
+ (facil_data->capacity * sizeof(struct connection_data_s)));
196
+ facil_data = NULL;
197
+ }
198
+ spn_unlock(&facil_libinit_lock);
199
+ }
200
+
201
+ static void facil_lib_init(void) {
202
+ ssize_t capa = sock_max_capacity();
203
+ if (capa < 0)
204
+ perror("ERROR: socket capacity unknown / failure"), exit(ENOMEM);
205
+ size_t mem_size =
206
+ sizeof(*facil_data) + (capa * sizeof(struct connection_data_s));
207
+ spn_lock(&facil_libinit_lock);
208
+ if (facil_data)
209
+ goto finish;
210
+ facil_data = mmap(NULL, mem_size, PROT_READ | PROT_WRITE | PROT_EXEC,
211
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
212
+ if (!facil_data)
213
+ perror("ERROR: Couldn't initialize the facil.io library"), exit(0);
214
+ memset(facil_data, 0, mem_size);
215
+ *facil_data = (struct facil_data_s){.capacity = capa, .parent = getpid()};
216
+ atexit(facil_libcleanup);
217
+ #ifdef DEBUG
218
+ if (FACIL_PRINT_STATE)
219
+ fprintf(stderr,
220
+ "Initialized the facil.io library.\n"
221
+ "facil.io's memory footprint per connection == %lu Bytes X %lu\n"
222
+ "=== facil.io's memory footprint: %lu ===\n\n",
223
+ sizeof(struct connection_data_s), facil_data->capacity, mem_size);
224
+ #endif
225
+ finish:
226
+ spn_unlock(&facil_libinit_lock);
227
+ time(&facil_data->last_cycle);
228
+ }
229
+
230
+ /* *****************************************************************************
231
+ Mock Protocol and service Callbacks
232
+ ***************************************************************************** */
233
+ static void mock_on_ev(intptr_t uuid, protocol_s *protocol) {
234
+ (void)uuid;
235
+ (void)protocol;
236
+ }
237
+
238
+ static void mock_on_close(protocol_s *protocol) { (void)(protocol); }
239
+
240
+ static void mock_ping(intptr_t uuid, protocol_s *protocol) {
241
+ (void)(protocol);
242
+ sock_force_close(uuid);
243
+ }
244
+ static void mock_idle(void) {}
245
+
246
+ /* *****************************************************************************
247
+ The listenning protocol
248
+ ***************************************************************************** */
249
+ #undef facil_listen
250
+
251
+ static const char *listener_protocol_name =
252
+ "listening protocol __facil_internal__";
253
+
254
+ struct ListenerProtocol {
255
+ protocol_s protocol;
256
+ protocol_s *(*on_open)(intptr_t uuid, void *udata);
257
+ void *udata;
258
+ void (*on_start)(void *udata);
259
+ void (*on_finish)(void *udata);
260
+ const char *port;
261
+ };
262
+
263
+ static void listener_ping(intptr_t uuid, protocol_s *plistener) {
264
+ // fprintf(stderr, "*** Listener Ping Called for %ld\n", sock_uuid2fd(uuid));
265
+ uuid_data(uuid).active = facil_data->last_cycle;
266
+ return;
267
+ (void)plistener;
268
+ }
269
+
270
+ static void listener_deferred_on_open(void *uuid_, void *srv_uuid_) {
271
+ intptr_t uuid = (intptr_t)uuid_;
272
+ intptr_t srv_uuid = (intptr_t)srv_uuid_;
273
+ struct ListenerProtocol *listener =
274
+ (struct ListenerProtocol *)protocol_try_lock(sock_uuid2fd(srv_uuid),
275
+ FIO_PR_LOCK_WRITE);
276
+ if (!listener) {
277
+ if (errno != EBADF)
278
+ defer(listener_deferred_on_open, uuid_, srv_uuid_);
279
+ return;
280
+ }
281
+ protocol_s *pr = listener->on_open(uuid, listener->udata);
282
+ facil_attach(uuid, pr);
283
+ if (!pr)
284
+ sock_close(uuid);
285
+ protocol_unlock((protocol_s *)listener, FIO_PR_LOCK_WRITE);
286
+ }
287
+
288
+ static void listener_on_data(intptr_t uuid, protocol_s *plistener) {
289
+ intptr_t new_client;
290
+ if ((new_client = sock_accept(uuid)) == -1) {
291
+ if (errno == ECONNABORTED || errno == ECONNRESET)
292
+ defer(deferred_on_data, (void *)uuid, NULL);
293
+ else if (errno != EWOULDBLOCK)
294
+ perror("ERROR: socket accept error");
295
+ return;
296
+ }
297
+ defer(listener_deferred_on_open, (void *)new_client, (void *)uuid);
298
+ defer(deferred_on_data, (void *)uuid, NULL);
299
+ // // Was, without `deferred_on_data`
300
+ // struct ListenerProtocol *listener = (void *)plistener;
301
+ // protocol_s *pr = listener->on_open(new_client, listener->udata);
302
+ // facil_attach(new_client, pr);
303
+ // if (!pr)
304
+ // sock_close(new_client);
305
+ return;
306
+ (void)plistener;
307
+ }
308
+
309
+ static void free_listenner(void *li) { free(li); }
310
+
311
+ static void listener_on_close(protocol_s *plistener) {
312
+ struct ListenerProtocol *listener = (void *)plistener;
313
+ listener->on_finish(listener->udata);
314
+ if (FACIL_PRINT_STATE)
315
+ fprintf(stderr, "* (%d) Stopped listening on port %s\n", getpid(),
316
+ listener->port);
317
+ free_listenner(listener);
318
+ }
319
+
320
+ static inline struct ListenerProtocol *
321
+ listener_alloc(struct facil_listen_args settings) {
322
+ if (!settings.on_start)
323
+ settings.on_start = (void (*)(void *))mock_on_close;
324
+ if (!settings.on_finish)
325
+ settings.on_finish = (void (*)(void *))mock_on_close;
326
+ struct ListenerProtocol *listener = malloc(sizeof(*listener));
327
+ if (listener) {
328
+ *listener = (struct ListenerProtocol){
329
+ .protocol.service = listener_protocol_name,
330
+ .protocol.on_data = listener_on_data,
331
+ .protocol.on_close = listener_on_close,
332
+ .protocol.ping = listener_ping,
333
+ .on_open = settings.on_open,
334
+ .udata = settings.udata,
335
+ .on_start = settings.on_start,
336
+ .on_finish = settings.on_finish,
337
+ .port = settings.port,
338
+ };
339
+ return listener;
340
+ }
341
+ return NULL;
342
+ }
343
+
344
+ inline static void listener_on_start(size_t fd) {
345
+ intptr_t uuid = sock_fd2uuid(fd);
346
+ if (uuid < 0)
347
+ fprintf(stderr, "ERROR: listening socket dropped?\n"), exit(4);
348
+ if (evio_add(fd, (void *)uuid) < 0)
349
+ perror("Couldn't register listening socket"), exit(4);
350
+ fd_data(fd).active = facil_data->last_cycle;
351
+ // call the on_init callback
352
+ struct ListenerProtocol *listener =
353
+ (struct ListenerProtocol *)uuid_data(uuid).protocol;
354
+ listener->on_start(listener->udata);
355
+ }
356
+
357
+ /**
358
+ Listens to a server with the following server settings (which MUST include
359
+ a default protocol).
360
+
361
+ This method blocks the current thread until the server is stopped (either
362
+ though a `srv_stop` function or when a SIGINT/SIGTERM is received).
363
+ */
364
+ int facil_listen(struct facil_listen_args settings) {
365
+ if (!facil_data)
366
+ facil_lib_init();
367
+ if (settings.on_open == NULL || settings.port == NULL)
368
+ return -1;
369
+ intptr_t uuid = sock_listen(settings.address, settings.port);
370
+ if (uuid == -1) {
371
+ return -1;
372
+ }
373
+ protocol_s *protocol = (void *)listener_alloc(settings);
374
+ facil_attach(uuid, protocol);
375
+ if (!protocol) {
376
+ sock_close(uuid);
377
+ return -1;
378
+ }
379
+ if (FACIL_PRINT_STATE)
380
+ fprintf(stderr, "* Listening on port %s\n", settings.port);
381
+ return 0;
382
+ }
383
+
384
+ /* *****************************************************************************
385
+ Connect (as client)
386
+ ***************************************************************************** */
387
+
388
+ static const char *connector_protocol_name = "connect protocol __internal__";
389
+
390
+ struct ConnectProtocol {
391
+ protocol_s protocol;
392
+ protocol_s *(*on_connect)(intptr_t uuid, void *udata);
393
+ void (*on_fail)(void *udata);
394
+ void *udata;
395
+ int opened;
396
+ };
397
+
398
+ static void connector_on_ready(intptr_t uuid, protocol_s *_connector) {
399
+ struct ConnectProtocol *connector = (void *)_connector;
400
+ connector->opened = 1;
401
+ // fprintf(stderr, "connector_on_ready called\n");
402
+ if (connector->on_connect) {
403
+ sock_touch(uuid);
404
+ if (facil_attach(uuid, connector->on_connect(uuid, connector->udata)) == -1)
405
+ goto error;
406
+ uuid_data(uuid).protocol->on_ready(uuid, uuid_data(uuid).protocol);
407
+ return;
408
+ }
409
+ error:
410
+ sock_close(uuid);
411
+ }
412
+
413
+ static void connector_on_data(intptr_t uuid, protocol_s *connector) {
414
+ (void)connector;
415
+ defer(deferred_on_data, (void *)uuid, NULL);
416
+ }
417
+
418
+ static void connector_on_close(protocol_s *pconnector) {
419
+ struct ConnectProtocol *connector = (void *)pconnector;
420
+ if (connector->opened == 0 && connector->on_fail)
421
+ connector->on_fail(connector->udata);
422
+ free(connector);
423
+ }
424
+
425
+ #undef facil_connect
426
+ intptr_t facil_connect(struct facil_connect_args opt) {
427
+ if (!opt.address || !opt.port || !opt.on_connect)
428
+ return -1;
429
+ if (!facil_data->last_cycle)
430
+ time(&facil_data->last_cycle);
431
+ struct ConnectProtocol *connector = malloc(sizeof(*connector));
432
+ *connector = (struct ConnectProtocol){
433
+ .on_connect = opt.on_connect,
434
+ .on_fail = opt.on_fail,
435
+ .udata = opt.udata,
436
+ .protocol.service = connector_protocol_name,
437
+ .protocol.on_data = connector_on_data,
438
+ .protocol.on_ready = connector_on_ready,
439
+ .protocol.on_close = connector_on_close,
440
+ .opened = 0,
441
+ };
442
+ if (!connector)
443
+ return -1;
444
+ intptr_t uuid = sock_connect(opt.address, opt.port);
445
+ if (uuid == -1)
446
+ return -1;
447
+ if (facil_attach(uuid, &connector->protocol) == -1) {
448
+ sock_close(uuid);
449
+ return -1;
450
+ }
451
+ return uuid;
452
+ }
453
+
454
+ /* *****************************************************************************
455
+ Timers
456
+ ***************************************************************************** */
457
+
458
+ /* *******
459
+ Timer Protocol
460
+ ******* */
461
+ typedef struct {
462
+ protocol_s protocol;
463
+ size_t milliseconds;
464
+ size_t repetitions;
465
+ void (*task)(void *);
466
+ void (*on_finish)(void *);
467
+ void *arg;
468
+ } timer_protocol_s;
469
+
470
+ #define prot2timer(protocol) (*((timer_protocol_s *)(protocol)))
471
+
472
+ const char *timer_protocol_name = "timer protocol __facil_internal__";
473
+
474
+ static void timer_on_data(intptr_t uuid, protocol_s *protocol) {
475
+ prot2timer(protocol).task(prot2timer(protocol).arg);
476
+ evio_reset_timer(sock_uuid2fd(uuid));
477
+ if (prot2timer(protocol).repetitions == 0)
478
+ return;
479
+ prot2timer(protocol).repetitions -= 1;
480
+ if (prot2timer(protocol).repetitions)
481
+ return;
482
+ evio_remove(sock_uuid2fd(uuid));
483
+ sock_force_close(uuid);
484
+ }
485
+
486
+ static void timer_on_close(protocol_s *protocol) {
487
+ prot2timer(protocol).on_finish(prot2timer(protocol).arg);
488
+ free(protocol);
489
+ }
490
+
491
+ static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
492
+ size_t milliseconds,
493
+ size_t repetitions,
494
+ void (*on_finish)(void *)) {
495
+ if (!on_finish)
496
+ on_finish = (void (*)(void *))mock_on_close;
497
+ timer_protocol_s *t = malloc(sizeof(*t));
498
+ if (t)
499
+ *t = (timer_protocol_s){
500
+ .protocol.service = timer_protocol_name,
501
+ .protocol.on_data = timer_on_data,
502
+ .protocol.on_close = timer_on_close,
503
+ .arg = arg,
504
+ .task = task,
505
+ .on_finish = on_finish,
506
+ .milliseconds = milliseconds,
507
+ .repetitions = repetitions,
508
+ };
509
+ return t;
510
+ }
511
+
512
+ inline static void timer_on_server_start(int fd) {
513
+ if (evio_add_timer(fd, (void *)sock_fd2uuid(fd),
514
+ prot2timer(fd_data(fd).protocol).milliseconds))
515
+ perror("Couldn't register a required timed event."), exit(4);
516
+ }
517
+
518
+ /**
519
+ * Creates a system timer (at the cost of 1 file descriptor).
520
+ *
521
+ * The task will repeat `repetitions` times. If `repetitions` is set to 0, task
522
+ * will repeat forever.
523
+ *
524
+ * Returns -1 on error or the new file descriptor on succeess.
525
+ */
526
+ int facil_run_every(size_t milliseconds, size_t repetitions,
527
+ void (*task)(void *), void *arg,
528
+ void (*on_finish)(void *)) {
529
+ if (task == NULL)
530
+ return -1;
531
+ timer_protocol_s *protocol = NULL;
532
+ intptr_t uuid = -1;
533
+ int fd = evio_open_timer();
534
+ if (fd == -1) {
535
+ perror("ERROR: couldn't create a timer fd");
536
+ goto error;
537
+ }
538
+ uuid = sock_open(fd);
539
+ if (uuid == -1)
540
+ goto error;
541
+ protocol = timer_alloc(task, arg, milliseconds, repetitions, on_finish);
542
+ if (protocol == NULL)
543
+ goto error;
544
+ facil_attach(uuid, (protocol_s *)protocol);
545
+ if (evio_isactive() && evio_add_timer(fd, (void *)uuid, milliseconds) < 0)
546
+ goto error;
547
+ return 0;
548
+ error:
549
+ if (uuid != -1)
550
+ sock_close(uuid);
551
+ else if (fd != -1)
552
+ close(fd);
553
+ return -1;
554
+ }
555
+
556
+ /* *****************************************************************************
557
+ Running the server
558
+ ***************************************************************************** */
559
+
560
+ static void print_pid(void *arg, void *ignr) {
561
+ (void)arg;
562
+ (void)ignr;
563
+ fprintf(stderr, "* %d is running.\n", getpid());
564
+ }
565
+
566
+ static void facil_review_timeout(void *arg, void *ignr) {
567
+ (void)ignr;
568
+ protocol_s *tmp;
569
+ time_t review = facil_data->last_cycle;
570
+ intptr_t fd = (intptr_t)arg;
571
+
572
+ uint16_t timeout = fd_data(fd).timeout;
573
+ if (!timeout)
574
+ timeout = 300; /* enforced timout settings */
575
+
576
+ if (!fd_data(fd).protocol || (fd_data(fd).active + timeout >= review))
577
+ goto finish;
578
+ tmp = protocol_try_lock(fd, FIO_PR_LOCK_STATE);
579
+ if (!tmp)
580
+ goto reschedule;
581
+ if (prt_meta(tmp).locks[FIO_PR_LOCK_TASK] ||
582
+ prt_meta(tmp).locks[FIO_PR_LOCK_WRITE])
583
+ goto unlock;
584
+ defer(deferred_ping, (void *)sock_fd2uuid(fd), NULL);
585
+ unlock:
586
+ protocol_unlock(tmp, FIO_PR_LOCK_STATE);
587
+ finish:
588
+ do {
589
+ fd++;
590
+ } while (!fd_data(fd).protocol && (fd < facil_data->capacity));
591
+
592
+ if (facil_data->capacity <= fd) {
593
+ facil_data->need_review = 1;
594
+ return;
595
+ }
596
+ reschedule:
597
+ defer(facil_review_timeout, (void *)fd, NULL);
598
+ }
599
+
600
+ static void facil_cycle(void *arg, void *ignr) {
601
+ (void)ignr;
602
+ static int idle = 0;
603
+ time(&facil_data->last_cycle);
604
+ int events = evio_review(defer_has_queue() ? 0 : 512);
605
+ if (events < 0)
606
+ goto error;
607
+ if (events > 0) {
608
+ idle = 1;
609
+ goto finish;
610
+ }
611
+ if (idle) {
612
+ ((struct facil_run_args *)arg)->on_idle();
613
+ idle = 0;
614
+ }
615
+ finish:
616
+ if (!defer_fork_is_active())
617
+ return;
618
+ if (facil_data->need_review) {
619
+ facil_data->need_review = 0;
620
+ defer(facil_review_timeout, (void *)0, NULL);
621
+ }
622
+ defer(facil_cycle, arg, NULL);
623
+ error:
624
+ (void)1;
625
+ }
626
+
627
+ static void facil_init_run(void *arg, void *arg2) {
628
+ (void)arg;
629
+ (void)arg2;
630
+ evio_create();
631
+ time(&facil_data->last_cycle);
632
+ for (intptr_t i = 0; i < facil_data->capacity; i++) {
633
+ if (fd_data(i).protocol) {
634
+ if (fd_data(i).protocol->service == listener_protocol_name)
635
+ listener_on_start(i);
636
+ else if (fd_data(i).protocol->service == timer_protocol_name)
637
+ timer_on_server_start(i);
638
+ else
639
+ evio_add(i, (void *)sock_fd2uuid(i));
640
+ }
641
+ }
642
+ facil_data->need_review = 1;
643
+ defer(facil_cycle, arg, NULL);
644
+ }
645
+
646
+ static void facil_cleanup(void *arg) {
647
+ fprintf(stderr, "* %d cleanning up.\n", getpid());
648
+ intptr_t uuid;
649
+ for (intptr_t i = 0; i < facil_data->capacity; i++) {
650
+ if (fd_data(i).protocol && (uuid = sock_fd2uuid(i)) >= 0) {
651
+ defer(deferred_on_shutdown, (void *)uuid, NULL);
652
+ }
653
+ }
654
+ facil_cycle(arg, NULL);
655
+ defer_perform();
656
+ facil_cycle(arg, NULL);
657
+ ((struct facil_run_args *)arg)->on_finish();
658
+ defer_perform();
659
+ }
660
+
661
+ #undef facil_run
662
+ void facil_run(struct facil_run_args args) {
663
+ signal(SIGPIPE, SIG_IGN);
664
+ if (!facil_data)
665
+ facil_lib_init();
666
+ if (!args.on_idle)
667
+ args.on_idle = mock_idle;
668
+ if (!args.on_finish)
669
+ args.on_finish = mock_idle;
670
+ #ifdef _SC_NPROCESSORS_ONLN
671
+ if (!args.threads && !args.processes) {
672
+ ssize_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
673
+ if (cpu_count > 0)
674
+ args.threads = args.processes = cpu_count;
675
+ }
676
+ #endif
677
+ if (!args.processes)
678
+ args.processes = 1;
679
+ if (!args.threads)
680
+ args.threads = 1;
681
+ if (FACIL_PRINT_STATE) {
682
+ fprintf(stderr, "Server is running %u %s X %u %s, press ^C to stop\n",
683
+ args.processes, args.processes > 1 ? "workers" : "worker",
684
+ args.threads, args.threads > 1 ? "threads" : "thread");
685
+ defer(print_pid, NULL, NULL);
686
+ }
687
+ defer(facil_init_run, &args, NULL);
688
+ int frk = defer_perform_in_fork(args.processes, args.threads);
689
+ facil_cleanup(&args);
690
+ if (frk < 0) {
691
+ perror("ERROR: couldn't spawn workers");
692
+ } else if (frk > 0) {
693
+ exit(0);
694
+ }
695
+ if (FACIL_PRINT_STATE)
696
+ fprintf(stderr, "\n --- Completed Shutdown ---\n");
697
+ }
698
+ /* *****************************************************************************
699
+ Setting the protocol
700
+ ***************************************************************************** */
701
+
702
+ /** Attaches (or updates) a protocol object to a socket UUID.
703
+ * Returns -1 on error and 0 on success.
704
+ */
705
+ int facil_attach(intptr_t uuid, protocol_s *protocol) {
706
+ if (!facil_data)
707
+ facil_lib_init();
708
+ if (protocol) {
709
+ if (!protocol->on_close)
710
+ protocol->on_close = mock_on_close;
711
+ if (!protocol->on_data)
712
+ protocol->on_data = mock_on_ev;
713
+ if (!protocol->on_ready)
714
+ protocol->on_ready = mock_on_ev;
715
+ if (!protocol->ping)
716
+ protocol->ping = mock_ping;
717
+ if (!protocol->on_shutdown)
718
+ protocol->on_shutdown = mock_on_ev;
719
+ protocol->rsv = 0;
720
+ }
721
+ if (!sock_isvalid(uuid))
722
+ return -1;
723
+ spn_lock(&uuid_data(uuid).lock);
724
+ protocol_s *old_protocol = uuid_data(uuid).protocol;
725
+ uuid_data(uuid).protocol = protocol;
726
+ uuid_data(uuid).active = facil_data->last_cycle;
727
+ spn_unlock(&uuid_data(uuid).lock);
728
+ if (old_protocol)
729
+ defer(deferred_on_close, old_protocol, NULL);
730
+ if (evio_isactive())
731
+ evio_add(sock_uuid2fd(uuid), (void *)uuid);
732
+ return 0;
733
+ }
734
+
735
+ /** Sets a timeout for a specific connection (if active). */
736
+ void facil_set_timeout(intptr_t uuid, uint8_t timeout) {
737
+ if (sock_isvalid(uuid)) {
738
+ uuid_data(uuid).active = facil_data->last_cycle;
739
+ uuid_data(uuid).timeout = timeout;
740
+ }
741
+ }
742
+ /** Gets a timeout for a specific connection. Returns 0 if there's no set
743
+ * timeout or the connection is inactive. */
744
+ uint8_t facil_get_timeout(intptr_t uuid) { return uuid_data(uuid).timeout; }
745
+
746
+ /* *****************************************************************************
747
+ Misc helpers
748
+ ***************************************************************************** */
749
+
750
+ /**
751
+ Returns the last time the server reviewed any pending IO events.
752
+ */
753
+ time_t facil_last_tick(void) { return facil_data->last_cycle; }
754
+
755
+ /**
756
+ * This function allows out-of-task access to a connection's `protocol_s` object
757
+ * by attempting to lock it.
758
+ */
759
+ protocol_s *facil_protocol_try_lock(intptr_t uuid,
760
+ enum facil_protocol_lock_e type) {
761
+ if (sock_isvalid(uuid) || !uuid_data(uuid).protocol) {
762
+ errno = EBADF;
763
+ return NULL;
764
+ }
765
+ return protocol_try_lock(sock_uuid2fd(uuid), type);
766
+ }
767
+ /** See `facil_protocol_try_lock` for details. */
768
+ void facil_protocol_unlock(protocol_s *pr, enum facil_protocol_lock_e type) {
769
+ if (!pr)
770
+ return;
771
+ protocol_unlock(pr, type);
772
+ }
773
+ /** Counts all the connections of a specific type. */
774
+ size_t facil_count(void *service) {
775
+ long count = 0;
776
+ void *tmp;
777
+ for (intptr_t i = 0; i < facil_data->capacity; i++) {
778
+ tmp = NULL;
779
+ spn_lock(&fd_data(i).lock);
780
+ if (fd_data(i).protocol && fd_data(i).protocol->service)
781
+ tmp = (void *)fd_data(i).protocol->service;
782
+ spn_unlock(&fd_data(i).lock);
783
+ if (tmp != listener_protocol_name && tmp != timer_protocol_name &&
784
+ (!service || (tmp == service)))
785
+ count++;
786
+ }
787
+ return count;
788
+ }
789
+
790
+ /* *****************************************************************************
791
+ Task Management - `facil_defer`, `facil_each`
792
+ ***************************************************************************** */
793
+
794
+ struct task {
795
+ intptr_t origin;
796
+ void (*func)(intptr_t uuid, protocol_s *, void *arg);
797
+ void *arg;
798
+ void (*on_done)(intptr_t uuid, void *arg);
799
+ const void *service;
800
+ uint32_t count;
801
+ enum facil_protocol_lock_e task_type;
802
+ spn_lock_i lock;
803
+ };
804
+
805
+ static inline struct task *alloc_facil_task(void) {
806
+ return malloc(sizeof(struct task));
807
+ }
808
+
809
+ static inline void free_facil_task(struct task *task) { free(task); }
810
+
811
+ static void mock_on_task_done(intptr_t uuid, void *arg) {
812
+ (void)uuid;
813
+ (void)arg;
814
+ }
815
+
816
+ static void perform_single_task(void *v_uuid, void *v_task) {
817
+ struct task *task = v_task;
818
+ if (!uuid_data(v_uuid).protocol)
819
+ goto fallback;
820
+ protocol_s *pr = protocol_try_lock(sock_uuid2fd(v_uuid), task->task_type);
821
+ if (!pr)
822
+ goto defer;
823
+ task->func((intptr_t)v_uuid, pr, task->arg);
824
+ protocol_unlock(pr, task->task_type);
825
+ free_facil_task(task);
826
+ return;
827
+ fallback:
828
+ task->on_done((intptr_t)v_uuid, task->arg);
829
+ return;
830
+ defer:
831
+ defer(perform_single_task, v_uuid, v_task);
832
+ return;
833
+ }
834
+
835
+ static void finish_multi_task(void *v_fd, void *v_task) {
836
+ struct task *task = v_task;
837
+ if (spn_trylock(&task->lock))
838
+ goto reschedule;
839
+ task->count--;
840
+ if (task->count) {
841
+ spn_unlock(&task->lock);
842
+ return;
843
+ }
844
+ task->on_done(task->origin, task->arg);
845
+ free_facil_task(task);
846
+ return;
847
+ reschedule:
848
+ defer(finish_multi_task, v_fd, v_task);
849
+ }
850
+
851
+ static void perform_multi_task(void *v_fd, void *v_task) {
852
+ if (!fd_data((intptr_t)v_fd).protocol) {
853
+ finish_multi_task(v_fd, v_task);
854
+ return;
855
+ }
856
+ struct task *task = v_task;
857
+ protocol_s *pr = protocol_try_lock((intptr_t)v_fd, task->task_type);
858
+ if (!pr)
859
+ goto reschedule;
860
+ if (pr->service == task->service)
861
+ task->func(sock_fd2uuid((intptr_t)v_fd), pr, task->arg);
862
+ protocol_unlock(pr, task->task_type);
863
+ defer(finish_multi_task, v_fd, v_task);
864
+ return;
865
+ reschedule:
866
+ // fprintf(stderr, "rescheduling multi for %p\n", v_fd);
867
+ defer(perform_multi_task, v_fd, v_task);
868
+ }
869
+
870
+ static void schedule_multi_task(void *v_fd, void *v_task) {
871
+ struct task *task = v_task;
872
+ intptr_t fd = (intptr_t)v_fd;
873
+ for (size_t i = 0; i < 64; i++) {
874
+ if (!fd_data(fd).protocol)
875
+ goto finish;
876
+ if (spn_trylock(&fd_data(fd).lock))
877
+ goto reschedule;
878
+ if (!fd_data(fd).protocol ||
879
+ fd_data(fd).protocol->service != task->service || fd == task->origin) {
880
+ spn_unlock(&fd_data(fd).lock);
881
+ goto finish;
882
+ }
883
+ spn_unlock(&fd_data(fd).lock);
884
+ spn_lock(&task->lock);
885
+ task->count++;
886
+ spn_unlock(&task->lock);
887
+ defer(perform_multi_task, (void *)fd, task);
888
+ finish:
889
+ do {
890
+ fd++;
891
+ } while (!fd_data(fd).protocol && (fd < facil_data->capacity));
892
+ if (fd >= (intptr_t)facil_data->capacity)
893
+ goto complete;
894
+ }
895
+ reschedule:
896
+ schedule_multi_task((void *)fd, v_task);
897
+ return;
898
+ complete:
899
+ defer(finish_multi_task, NULL, v_task);
900
+ }
901
+ /**
902
+ * Schedules a protected connection task. The task will run within the
903
+ * connection's lock.
904
+ *
905
+ * If the connection is closed before the task can run, the
906
+ * `fallback` task wil be called instead, allowing for resource cleanup.
907
+ */
908
+ #undef facil_defer
909
+ void facil_defer(struct facil_defer_args_s args) {
910
+ if (!args.fallback)
911
+ args.fallback = mock_on_task_done;
912
+ if (!args.task_type)
913
+ args.task_type = FIO_PR_LOCK_TASK;
914
+ if (!args.task || !uuid_data(args.uuid).protocol || args.uuid < 0 ||
915
+ !sock_isvalid(args.uuid))
916
+ goto error;
917
+ struct task *task = alloc_facil_task();
918
+ if (!task)
919
+ goto error;
920
+ *task = (struct task){
921
+ .func = args.task, .arg = args.arg, .on_done = args.fallback};
922
+ defer(perform_single_task, (void *)args.uuid, task);
923
+ return;
924
+ error:
925
+ defer((void (*)(void *, void *))args.fallback, (void *)args.uuid, args.arg);
926
+ }
927
+
928
+ /**
929
+ * Schedules a protected connection task for each `service` connection.
930
+ * The tasks will run within each of the connection's locks.
931
+ *
932
+ * Once all the tasks were performed, the `on_complete` callback will be called.
933
+ */
934
+ #undef facil_each
935
+ int facil_each(struct facil_each_args_s args) {
936
+ if (!args.on_complete)
937
+ args.on_complete = mock_on_task_done;
938
+ if (!args.task_type)
939
+ args.task_type = FIO_PR_LOCK_TASK;
940
+ if (!args.task)
941
+ goto error;
942
+ struct task *task = alloc_facil_task();
943
+ if (!task)
944
+ goto error;
945
+ *task = (struct task){.origin = args.origin,
946
+ .func = args.task,
947
+ .arg = args.arg,
948
+ .on_done = args.on_complete,
949
+ .service = args.service,
950
+ .task_type = args.task_type,
951
+ .count = 1};
952
+ defer(schedule_multi_task, (void *)0, task);
953
+ return 0;
954
+ error:
955
+ defer((void (*)(void *, void *))args.on_complete, (void *)args.origin,
956
+ args.arg);
957
+ return -1;
958
+ }