iodine 0.3.6 → 0.4.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LIMITS.md +25 -0
  4. data/README.md +39 -80
  5. data/SPEC-Websocket-Draft.md +129 -4
  6. data/bin/echo +2 -2
  7. data/bin/http-hello +1 -0
  8. data/bin/updated api +113 -0
  9. data/bin/ws-echo +0 -1
  10. data/examples/broadcast.ru +56 -0
  11. data/examples/echo.ru +57 -0
  12. data/examples/hello.ru +30 -0
  13. data/examples/redis.ru +69 -0
  14. data/examples/shootout.ru +53 -0
  15. data/exe/iodine +2 -80
  16. data/ext/iodine/defer.c +11 -5
  17. data/ext/iodine/empty.h +26 -0
  18. data/ext/iodine/evio.h +1 -1
  19. data/ext/iodine/facil.c +103 -61
  20. data/ext/iodine/facil.h +20 -12
  21. data/ext/iodine/fio_dict.c +446 -0
  22. data/ext/iodine/fio_dict.h +90 -0
  23. data/ext/iodine/fio_hash_table.h +370 -0
  24. data/ext/iodine/fio_list.h +30 -3
  25. data/ext/iodine/http.c +169 -37
  26. data/ext/iodine/http.h +33 -10
  27. data/ext/iodine/http1.c +78 -42
  28. data/ext/iodine/http_request.c +6 -0
  29. data/ext/iodine/http_request.h +3 -0
  30. data/ext/iodine/http_response.c +43 -11
  31. data/ext/iodine/iodine.c +380 -0
  32. data/ext/iodine/iodine.h +62 -0
  33. data/ext/iodine/iodine_helpers.c +235 -0
  34. data/ext/iodine/iodine_helpers.h +13 -0
  35. data/ext/iodine/iodine_http.c +409 -241
  36. data/ext/iodine/iodine_http.h +7 -14
  37. data/ext/iodine/iodine_protocol.c +626 -0
  38. data/ext/iodine/iodine_protocol.h +13 -0
  39. data/ext/iodine/iodine_pubsub.c +646 -0
  40. data/ext/iodine/iodine_pubsub.h +27 -0
  41. data/ext/iodine/iodine_websockets.c +796 -0
  42. data/ext/iodine/iodine_websockets.h +19 -0
  43. data/ext/iodine/pubsub.c +544 -0
  44. data/ext/iodine/pubsub.h +215 -0
  45. data/ext/iodine/random.c +4 -4
  46. data/ext/iodine/rb-call.c +1 -5
  47. data/ext/iodine/rb-defer.c +3 -20
  48. data/ext/iodine/rb-rack-io.c +22 -22
  49. data/ext/iodine/rb-rack-io.h +3 -4
  50. data/ext/iodine/rb-registry.c +111 -118
  51. data/ext/iodine/redis_connection.c +277 -0
  52. data/ext/iodine/redis_connection.h +77 -0
  53. data/ext/iodine/redis_engine.c +398 -0
  54. data/ext/iodine/redis_engine.h +68 -0
  55. data/ext/iodine/resp.c +842 -0
  56. data/ext/iodine/resp.h +253 -0
  57. data/ext/iodine/sock.c +26 -12
  58. data/ext/iodine/sock.h +14 -3
  59. data/ext/iodine/spnlock.inc +19 -2
  60. data/ext/iodine/websockets.c +299 -11
  61. data/ext/iodine/websockets.h +159 -6
  62. data/lib/iodine.rb +104 -1
  63. data/lib/iodine/cli.rb +106 -0
  64. data/lib/iodine/monkeypatch.rb +40 -0
  65. data/lib/iodine/pubsub.rb +70 -0
  66. data/lib/iodine/version.rb +1 -1
  67. data/lib/iodine/websocket.rb +12 -0
  68. data/lib/rack/handler/iodine.rb +33 -7
  69. metadata +35 -7
  70. data/ext/iodine/iodine_core.c +0 -760
  71. data/ext/iodine/iodine_core.h +0 -79
  72. data/ext/iodine/iodine_websocket.c +0 -551
  73. data/ext/iodine/iodine_websocket.h +0 -22
  74. data/lib/iodine/http.rb +0 -4
@@ -121,8 +121,8 @@ restart:
121
121
  spn_unlock(&deferred.lock);
122
122
  task.func(task.arg1, task.arg2);
123
123
  goto restart;
124
- } else
125
- spn_unlock(&deferred.lock);
124
+ }
125
+ spn_unlock(&deferred.lock);
126
126
  }
127
127
 
128
128
  /** returns true if there are deferred functions waiting for execution. */
@@ -186,15 +186,17 @@ struct defer_pool {
186
186
  void *threads[];
187
187
  };
188
188
 
189
- static void *defer_worker_thread(void *pool) {
189
+ static void *defer_worker_thread(void *pool_) {
190
+ volatile pool_pt pool = pool_;
190
191
  signal(SIGPIPE, SIG_IGN);
191
- size_t throttle = (((pool_pt)pool)->count) * DEFER_THROTTLE;
192
+ size_t throttle = (pool->count) * DEFER_THROTTLE;
192
193
  if (!throttle || throttle > 1572864UL)
193
194
  throttle = 1572864UL;
195
+ defer_perform();
194
196
  do {
195
197
  throttle_thread(throttle);
196
198
  defer_perform();
197
- } while (((pool_pt)pool)->flag);
199
+ } while (pool->flag);
198
200
  return NULL;
199
201
  }
200
202
 
@@ -285,6 +287,8 @@ int defer_perform_in_fork(unsigned int process_count,
285
287
  if (forked_pool)
286
288
  return -1; /* we're already running inside an active `fork` */
287
289
 
290
+ static struct defer_pool pool_placeholder = {.count = 1, .flag = 1};
291
+
288
292
  struct sigaction act, old, old_term, old_pipe;
289
293
  pid_t *pids = NULL;
290
294
  int ret = 0;
@@ -318,6 +322,7 @@ int defer_perform_in_fork(unsigned int process_count,
318
322
  for (pids_count = 0; pids_count < process_count; pids_count++) {
319
323
  if (!(pids[pids_count] = fork())) {
320
324
  defer_fork_pid_id = pids_count + 1;
325
+ forked_pool = &pool_placeholder;
321
326
  forked_pool = defer_pool_start(thread_count);
322
327
  defer_pool_wait(forked_pool);
323
328
  defer_perform();
@@ -330,6 +335,7 @@ int defer_perform_in_fork(unsigned int process_count,
330
335
  }
331
336
  }
332
337
  pids_count++;
338
+ forked_pool = &pool_placeholder;
333
339
  forked_pool = defer_pool_start(thread_count);
334
340
  defer_pool_wait(forked_pool);
335
341
  forked_pool = NULL;
@@ -0,0 +1,26 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT except for any non-public-domain algorithms (none that I'm aware
4
+ of), which might be subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef BSCRYPT_EMPTY_H
9
+ #define BSCRYPT_EMPTY_H
10
+ #include "bscrypt-common.h"
11
+ /* *****************************************************************************
12
+ C++ extern
13
+ */
14
+ #if defined(__cplusplus)
15
+ extern "C" {
16
+ #endif
17
+
18
+ /* *****************************************************************************
19
+ C++ extern finish
20
+ */
21
+ #if defined(__cplusplus)
22
+ }
23
+ #endif
24
+
25
+ /* ****************************************************************************/
26
+ #endif /* include guard */
@@ -47,7 +47,7 @@ Returns -1 on error, otherwise returns a unique value representing the `epoll`
47
47
  or `kqueue` object. The returned value can be safely ignored.
48
48
 
49
49
  NOTE: Once an `epoll` / `kqueue` object was opened, `fork` should be avoided,
50
- since ALL the events will be shared among the forked proccesses (while not ALL
50
+ since ALL the events will be shared among the forked processes (while not ALL
51
51
  the file descriptors are expected to be shared).
52
52
  */
53
53
  intptr_t evio_create(void);
@@ -83,15 +83,14 @@ inline static void protocol_unlock(protocol_s *pr,
83
83
  /* *****************************************************************************
84
84
  Deferred event handlers
85
85
  ***************************************************************************** */
86
- static void deferred_on_close(void *arg, void *arg2) {
87
- protocol_s *pr = arg;
86
+ static void deferred_on_close(void *uuid_, void *pr_) {
87
+ protocol_s *pr = pr_;
88
88
  if (pr->rsv)
89
89
  goto postpone;
90
- pr->on_close(pr);
90
+ pr->on_close((intptr_t)uuid_, pr);
91
91
  return;
92
92
  postpone:
93
- defer(deferred_on_close, arg, NULL);
94
- (void)arg2;
93
+ defer(deferred_on_close, uuid_, pr_);
95
94
  }
96
95
 
97
96
  static void deferred_on_ready(void *arg, void *arg2) {
@@ -181,7 +180,7 @@ void sock_on_close(intptr_t uuid) {
181
180
  clear_connection_data_unsafe(uuid, NULL);
182
181
  spn_unlock(&uuid_data(uuid).lock);
183
182
  if (old_protocol)
184
- defer(deferred_on_close, old_protocol, NULL);
183
+ defer(deferred_on_close, (void *)uuid, old_protocol);
185
184
  }
186
185
 
187
186
  void sock_touch(intptr_t uuid) {
@@ -235,14 +234,22 @@ finish:
235
234
  }
236
235
 
237
236
  /* *****************************************************************************
238
- Mock Protocol and service Callbacks
237
+ Mock Protocol Callbacks and Service Funcions
239
238
  ***************************************************************************** */
240
239
  static void mock_on_ev(intptr_t uuid, protocol_s *protocol) {
241
240
  (void)uuid;
242
241
  (void)protocol;
243
242
  }
244
243
 
245
- static void mock_on_close(protocol_s *protocol) { (void)(protocol); }
244
+ static void mock_on_close(intptr_t uuid, protocol_s *protocol) {
245
+ (void)(protocol);
246
+ (void)(uuid);
247
+ }
248
+
249
+ static void mock_on_finish(intptr_t uuid, void *udata) {
250
+ (void)(udata);
251
+ (void)(uuid);
252
+ }
246
253
 
247
254
  static void mock_ping(intptr_t uuid, protocol_s *protocol) {
248
255
  (void)(protocol);
@@ -250,6 +257,24 @@ static void mock_ping(intptr_t uuid, protocol_s *protocol) {
250
257
  }
251
258
  static void mock_idle(void) {}
252
259
 
260
+ /* Support for the default pub/sub cluster engine */
261
+ #pragma weak pubsub_cluster_init
262
+ void pubsub_cluster_init(void) {}
263
+
264
+ /*
265
+ * Support for the (removed) file caching service.
266
+ *
267
+ * It's possible to re-add the service from the `unused` folder.
268
+ */
269
+ #pragma weak fio_cfd_clear
270
+ void fio_cfd_clear(void) {}
271
+
272
+ /* perform initialization for external services. */
273
+ static void facil_external_init(void) { pubsub_cluster_init(); }
274
+
275
+ /* perform cleanup for external services. */
276
+ static void facil_external_cleanup(void) { fio_cfd_clear(); }
277
+
253
278
  /* *****************************************************************************
254
279
  The listenning protocol
255
280
  ***************************************************************************** */
@@ -264,9 +289,10 @@ struct ListenerProtocol {
264
289
  void *udata;
265
290
  void *rw_udata;
266
291
  sock_rw_hook_s *(*set_rw_hooks)(intptr_t uuid, void *udata);
267
- void (*on_start)(void *udata);
268
- void (*on_finish)(void *udata);
269
- const char *port;
292
+ void (*on_finish_rw)(intptr_t uuid, void *rw_udata);
293
+ void (*on_start)(intptr_t uuid, void *udata);
294
+ void (*on_finish)(intptr_t uuid, void *udata);
295
+ char port[16];
270
296
  };
271
297
 
272
298
  static sock_rw_hook_s *listener_set_rw_hooks(intptr_t uuid, void *udata) {
@@ -318,21 +344,16 @@ static void listener_on_data(intptr_t uuid, protocol_s *plistener) {
318
344
  }
319
345
  defer(listener_deferred_on_open, (void *)new_client, (void *)uuid);
320
346
  defer(deferred_on_data, (void *)uuid, NULL);
321
- // // Was, without `deferred_on_data`
322
- // struct ListenerProtocol *listener = (void *)plistener;
323
- // protocol_s *pr = listener->on_open(new_client, listener->udata);
324
- // facil_attach(new_client, pr);
325
- // if (!pr)
326
- // sock_close(new_client);
327
347
  return;
328
348
  (void)plistener;
329
349
  }
330
350
 
331
351
  static void free_listenner(void *li) { free(li); }
332
352
 
333
- static void listener_on_close(protocol_s *plistener) {
353
+ static void listener_on_close(intptr_t uuid, protocol_s *plistener) {
334
354
  struct ListenerProtocol *listener = (void *)plistener;
335
- listener->on_finish(listener->udata);
355
+ listener->on_finish(uuid, listener->udata);
356
+ listener->on_finish_rw(uuid, listener->rw_udata);
336
357
  if (FACIL_PRINT_STATE)
337
358
  fprintf(stderr, "* (%d) Stopped listening on port %s\n", getpid(),
338
359
  listener->port);
@@ -342,9 +363,11 @@ static void listener_on_close(protocol_s *plistener) {
342
363
  static inline struct ListenerProtocol *
343
364
  listener_alloc(struct facil_listen_args settings) {
344
365
  if (!settings.on_start)
345
- settings.on_start = (void (*)(void *))mock_on_close;
366
+ settings.on_start = mock_on_finish;
346
367
  if (!settings.on_finish)
347
- settings.on_finish = (void (*)(void *))mock_on_close;
368
+ settings.on_finish = (void (*)(intptr_t, void *))mock_on_close;
369
+ if (!settings.on_finish_rw)
370
+ settings.on_finish_rw = mock_on_finish;
348
371
  if (!settings.set_rw_hooks)
349
372
  settings.set_rw_hooks = listener_set_rw_hooks;
350
373
  struct ListenerProtocol *listener = malloc(sizeof(*listener));
@@ -358,10 +381,12 @@ listener_alloc(struct facil_listen_args settings) {
358
381
  .udata = settings.udata,
359
382
  .on_start = settings.on_start,
360
383
  .on_finish = settings.on_finish,
384
+ .on_finish_rw = settings.on_finish_rw,
361
385
  .set_rw_hooks = settings.set_rw_hooks,
362
386
  .rw_udata = settings.rw_udata,
363
- .port = settings.port,
364
387
  };
388
+ size_t tmp = strlen(settings.port);
389
+ memcpy(listener->port, settings.port, tmp + 1);
365
390
  return listener;
366
391
  }
367
392
  return NULL;
@@ -378,7 +403,7 @@ inline static void listener_on_start(size_t fd) {
378
403
  // call the on_init callback
379
404
  struct ListenerProtocol *listener =
380
405
  (struct ListenerProtocol *)uuid_data(uuid).protocol;
381
- listener->on_start(listener->udata);
406
+ listener->on_start(uuid, listener->udata);
382
407
  }
383
408
 
384
409
  /**
@@ -417,10 +442,11 @@ static const char *connector_protocol_name = "connect protocol __internal__";
417
442
  struct ConnectProtocol {
418
443
  protocol_s protocol;
419
444
  protocol_s *(*on_connect)(intptr_t uuid, void *udata);
420
- void (*on_fail)(void *udata);
445
+ void (*on_fail)(intptr_t uuid, void *udata);
421
446
  sock_rw_hook_s *(*set_rw_hooks)(intptr_t uuid, void *udata);
422
447
  void *udata;
423
448
  void *rw_udata;
449
+ intptr_t uuid;
424
450
  int opened;
425
451
  };
426
452
 
@@ -447,22 +473,23 @@ static void connector_on_data(intptr_t uuid, protocol_s *connector) {
447
473
  defer(deferred_on_data, (void *)uuid, NULL);
448
474
  }
449
475
 
450
- static void connector_on_close(protocol_s *pconnector) {
476
+ static void connector_on_close(intptr_t uuid, protocol_s *pconnector) {
451
477
  struct ConnectProtocol *connector = (void *)pconnector;
452
478
  if (connector->opened == 0 && connector->on_fail)
453
- connector->on_fail(connector->udata);
479
+ connector->on_fail(connector->uuid, connector->udata);
454
480
  free(connector);
481
+ (void)uuid;
455
482
  }
456
483
 
457
484
  #undef facil_connect
458
485
  intptr_t facil_connect(struct facil_connect_args opt) {
459
486
  if (!opt.address || !opt.port || !opt.on_connect)
460
- return -1;
487
+ goto error;
461
488
  if (!opt.set_rw_hooks)
462
489
  opt.set_rw_hooks = listener_set_rw_hooks;
463
- if (!facil_data->last_cycle)
464
- time(&facil_data->last_cycle);
465
490
  struct ConnectProtocol *connector = malloc(sizeof(*connector));
491
+ if (!connector)
492
+ goto error;
466
493
  *connector = (struct ConnectProtocol){
467
494
  .on_connect = opt.on_connect,
468
495
  .on_fail = opt.on_fail,
@@ -475,16 +502,18 @@ intptr_t facil_connect(struct facil_connect_args opt) {
475
502
  .rw_udata = opt.rw_udata,
476
503
  .opened = 0,
477
504
  };
478
- if (!connector)
479
- return -1;
480
- intptr_t uuid = sock_connect(opt.address, opt.port);
505
+ intptr_t uuid = connector->uuid = sock_connect(opt.address, opt.port);
481
506
  if (uuid == -1)
482
- return -1;
507
+ goto error;
483
508
  if (facil_attach(uuid, &connector->protocol) == -1) {
484
509
  sock_close(uuid);
485
510
  return -1;
486
511
  }
487
512
  return uuid;
513
+ error:
514
+ if (opt.on_fail)
515
+ opt.on_fail(uuid, opt.udata);
516
+ return -1;
488
517
  }
489
518
 
490
519
  /* *****************************************************************************
@@ -519,9 +548,10 @@ static void timer_on_data(intptr_t uuid, protocol_s *protocol) {
519
548
  sock_force_close(uuid);
520
549
  }
521
550
 
522
- static void timer_on_close(protocol_s *protocol) {
551
+ static void timer_on_close(intptr_t uuid, protocol_s *protocol) {
523
552
  prot2timer(protocol).on_finish(prot2timer(protocol).arg);
524
553
  free(protocol);
554
+ (void)uuid;
525
555
  }
526
556
 
527
557
  static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
@@ -606,15 +636,11 @@ static struct {
606
636
  .handlers.prev = &facil_cluster_data.handlers,
607
637
  .lock = SPN_LOCK_INIT};
608
638
 
609
- /* internal support for the default pub/sub cluster engin */
610
- #pragma weak pubsub_cluster_init
611
- void pubsub_cluster_init(void) {}
612
-
613
639
  /* message handler */
614
640
  typedef struct {
615
641
  fio_list_s list;
616
642
  void (*on_message)(void *data, uint32_t len);
617
- uint32_t msg_type;
643
+ int32_t msg_type;
618
644
  } fio_cluster_handler;
619
645
 
620
646
  /* message sending and protocol. */
@@ -623,7 +649,7 @@ struct facil_cluster_msg_packet {
623
649
  spn_lock_i lock;
624
650
  struct facil_cluster_msg {
625
651
  uint32_t len;
626
- uint32_t msg_type;
652
+ int32_t msg_type;
627
653
  } payload;
628
654
  uint8_t data[];
629
655
  };
@@ -653,7 +679,7 @@ static void facil_cluster_handle_msg(struct facil_cluster_msg *msg) {
653
679
  static char *facil_cluster_protocol_id = "facil_io_internal_cluster_protocol";
654
680
 
655
681
  /* cluster protocol on_close callback */
656
- static void facil_cluster_on_close(protocol_s *pr_) {
682
+ static void facil_cluster_on_close(intptr_t uuid, protocol_s *pr_) {
657
683
  struct facil_cluster_protocol *pr = (struct facil_cluster_protocol *)pr_;
658
684
  if (pr->msg)
659
685
  free(pr->msg);
@@ -663,6 +689,7 @@ static void facil_cluster_on_close(protocol_s *pr_) {
663
689
  fprintf(stderr, "ERROR: facil cluster member (%d) closed prematurely.\n",
664
690
  defer_fork_pid());
665
691
  #endif
692
+ (void)uuid;
666
693
  }
667
694
 
668
695
  /* cluster protocol on_data callback */
@@ -758,7 +785,6 @@ static void facil_cluster_init(uint16_t count) {
758
785
  facil_attach(facil_cluster_data.pipes[i].in, &stub_protocol);
759
786
  facil_attach(facil_cluster_data.pipes[i].out, &stub_protocol);
760
787
  }
761
- pubsub_cluster_init();
762
788
  defer(facil_cluster_register, NULL, NULL);
763
789
  return;
764
790
  error:
@@ -782,7 +808,7 @@ static void facil_cluster_destroy(void) {
782
808
  facil_cluster_data.pipes = NULL;
783
809
  }
784
810
 
785
- void facil_cluster_set_handler(uint32_t msg_type,
811
+ void facil_cluster_set_handler(int32_t msg_type,
786
812
  void (*on_message)(void *data, uint32_t len)) {
787
813
  fio_cluster_handler *hnd;
788
814
  spn_lock(&facil_cluster_data.lock);
@@ -810,7 +836,7 @@ static void facil_msg_free(void *msg_) {
810
836
  }
811
837
  free(msg);
812
838
  }
813
- int facil_cluster_send(uint32_t msg_type, void *data, uint32_t len) {
839
+ int facil_cluster_send(int32_t msg_type, void *data, uint32_t len) {
814
840
  if (!defer_fork_is_active()) {
815
841
  #ifdef DEBUG
816
842
  fprintf(stderr,
@@ -893,18 +919,24 @@ static void facil_cycle(void *arg, void *ignr) {
893
919
  (void)ignr;
894
920
  static int idle = 0;
895
921
  time(&facil_data->last_cycle);
896
- int events = evio_review(defer_has_queue() ? 0 : 512);
897
- if (events < 0)
898
- goto error;
899
- if (events > 0) {
900
- idle = 1;
901
- goto finish;
902
- }
903
- if (idle) {
904
- ((struct facil_run_args *)arg)->on_idle();
905
- idle = 0;
922
+ int events;
923
+ if (defer_has_queue()) {
924
+ events = evio_review(0);
925
+ if (events < 0)
926
+ goto error;
927
+ if (events > 0)
928
+ idle = 1;
929
+ } else {
930
+ events = evio_review(512);
931
+ if (events < 0)
932
+ goto error;
933
+ if (events > 0) {
934
+ idle = 1;
935
+ } else if (idle) {
936
+ ((struct facil_run_args *)arg)->on_idle();
937
+ idle = 0;
938
+ }
906
939
  }
907
- finish:
908
940
  if (!defer_fork_is_active())
909
941
  return;
910
942
  if (facil_data->need_review) {
@@ -932,6 +964,7 @@ static void facil_init_run(void *arg, void *arg2) {
932
964
  }
933
965
  }
934
966
  facil_data->need_review = 1;
967
+ facil_external_init();
935
968
  defer(facil_cycle, arg, NULL);
936
969
  }
937
970
 
@@ -950,6 +983,7 @@ static void facil_cleanup(void *arg) {
950
983
  defer_perform();
951
984
  evio_close();
952
985
  facil_cluster_destroy();
986
+ facil_external_cleanup();
953
987
  }
954
988
 
955
989
  #undef facil_run
@@ -1013,18 +1047,19 @@ int facil_attach(intptr_t uuid, protocol_s *protocol) {
1013
1047
  protocol->on_shutdown = mock_on_ev;
1014
1048
  protocol->rsv = 0;
1015
1049
  }
1050
+ spn_lock(&uuid_data(uuid).lock);
1016
1051
  if (!sock_isvalid(uuid)) {
1052
+ spn_unlock(&uuid_data(uuid).lock);
1017
1053
  if (protocol)
1018
- defer(deferred_on_close, protocol, NULL);
1054
+ defer(deferred_on_close, (void *)uuid, protocol);
1019
1055
  return -1;
1020
1056
  }
1021
- spn_lock(&uuid_data(uuid).lock);
1022
1057
  protocol_s *old_protocol = uuid_data(uuid).protocol;
1023
1058
  uuid_data(uuid).protocol = protocol;
1024
1059
  uuid_data(uuid).active = facil_data->last_cycle;
1025
1060
  spn_unlock(&uuid_data(uuid).lock);
1026
1061
  if (old_protocol)
1027
- defer(deferred_on_close, old_protocol, NULL);
1062
+ defer(deferred_on_close, (void *)uuid, old_protocol);
1028
1063
  if (evio_isactive())
1029
1064
  evio_add(sock_uuid2fd(uuid), (void *)uuid);
1030
1065
  return 0;
@@ -1048,11 +1083,13 @@ Misc helpers
1048
1083
  /**
1049
1084
  Returns the last time the server reviewed any pending IO events.
1050
1085
  */
1051
- time_t facil_last_tick(void) { return facil_data->last_cycle; }
1086
+ time_t facil_last_tick(void) {
1087
+ return facil_data ? facil_data->last_cycle : time(NULL);
1088
+ }
1052
1089
 
1053
1090
  /**
1054
- * This function allows out-of-task access to a connection's `protocol_s` object
1055
- * by attempting to lock it.
1091
+ * This function allows out-of-task access to a connection's `protocol_s`
1092
+ * object by attempting to lock it.
1056
1093
  */
1057
1094
  protocol_s *facil_protocol_try_lock(intptr_t uuid,
1058
1095
  enum facil_protocol_lock_e type) {
@@ -1118,12 +1155,17 @@ static void perform_single_task(void *v_uuid, void *v_task) {
1118
1155
  protocol_s *pr = protocol_try_lock(sock_uuid2fd(v_uuid), task->task_type);
1119
1156
  if (!pr)
1120
1157
  goto defer;
1158
+ if (pr->service == connector_protocol_name) {
1159
+ protocol_unlock(pr, task->task_type);
1160
+ goto defer;
1161
+ }
1121
1162
  task->func((intptr_t)v_uuid, pr, task->arg);
1122
1163
  protocol_unlock(pr, task->task_type);
1123
1164
  free_facil_task(task);
1124
1165
  return;
1125
1166
  fallback:
1126
1167
  task->on_done((intptr_t)v_uuid, task->arg);
1168
+ free_facil_task(task);
1127
1169
  return;
1128
1170
  defer:
1129
1171
  defer(perform_single_task, v_uuid, v_task);