iodine 0.7.1 → 0.7.2

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.

@@ -1,6 +1,6 @@
1
- #ifndef H_IODINE_JSON_H
2
- #define H_IODINE_JSON_H
1
+ #ifndef H_IODINE_MUSTACHE_H
2
+ #define H_IODINE_MUSTACHE_H
3
3
 
4
- void iodine_init_json(void);
4
+ void iodine_init_mustache(void);
5
5
 
6
6
  #endif
@@ -1,14 +1,16 @@
1
1
  #include "iodine.h"
2
2
 
3
- #include "fio_hashmap.h"
4
3
  #include "iodine_store.h"
5
4
 
6
- #include <fio.h>
7
5
  #include <inttypes.h>
8
6
  #include <stdint.h>
9
7
 
8
+ #define FIO_SET_NAME fio_hash
9
+ #define FIO_SET_OBJ_TYPE uintptr_t
10
+ #include <fio.h>
11
+
10
12
  fio_lock_i lock = FIO_LOCK_INIT;
11
- fio_hash_s storage = FIO_HASH_INIT;
13
+ fio_hash_s storage = FIO_SET_INIT;
12
14
 
13
15
  #ifndef IODINE_DEBUG
14
16
  #define IODINE_DEBUG 0
@@ -23,10 +25,8 @@ static VALUE storage_add(VALUE obj) {
23
25
  if (obj == Qnil || obj == Qtrue || obj == Qfalse)
24
26
  return obj;
25
27
  fio_lock(&lock);
26
- uintptr_t val = (uintptr_t)fio_hash_insert(&storage, obj, (void *)1);
27
- if (val) {
28
- fio_hash_insert(&storage, obj, (void *)(val + 1));
29
- }
28
+ uintptr_t *val = fio_hash_insert(&storage, obj, 0);
29
+ ++val[0];
30
30
  fio_unlock(&lock);
31
31
  return obj;
32
32
  }
@@ -36,14 +36,9 @@ static VALUE storage_remove(VALUE obj) {
36
36
  storage.count == 0)
37
37
  return obj;
38
38
  fio_lock(&lock);
39
- uintptr_t val = (uintptr_t)fio_hash_insert(&storage, obj, NULL);
40
- if (val > 1) {
41
- fio_hash_insert(&storage, obj, (void *)(val - 1));
42
- }
43
- if ((storage.count << 1) <= storage.pos &&
44
- (storage.pos << 1) > storage.capa) {
45
- fio_hash_compact(&storage);
46
- }
39
+ uintptr_t *val = fio_hash_find(&storage, obj, 0);
40
+ if (val && *val <= 1)
41
+ fio_hash_remove(&storage, obj, 0);
47
42
  fio_unlock(&lock);
48
43
  return obj;
49
44
  }
@@ -55,10 +50,10 @@ static void storage_print(void) {
55
50
  fprintf(stderr, "Ruby <=> C Memory storage stats (pid: %d):\n", getpid());
56
51
  fio_lock(&lock);
57
52
  uintptr_t index = 0;
58
- FIO_HASH_FOR_LOOP(&storage, pos) {
53
+ FIO_SET_FOR_LOOP(&storage, pos) {
59
54
  if (pos->obj) {
60
55
  fprintf(stderr, "[%" PRIuPTR "] => %" PRIuPTR " X obj %p type %d\n",
61
- index++, (uintptr_t)pos->obj, (void *)pos->key, TYPE(pos->key));
56
+ index++, pos->obj, (void *)pos->hash, TYPE(pos->hash));
62
57
  }
63
58
  }
64
59
  fprintf(stderr, "Total of %" PRIuPTR " objects protected form GC\n", index);
@@ -88,9 +83,9 @@ static void storage_mark(void *ignore) {
88
83
  #endif
89
84
  fio_lock(&lock);
90
85
  // fio_hash_compact(&storage);
91
- FIO_HASH_FOR_LOOP(&storage, pos) {
86
+ FIO_SET_FOR_LOOP(&storage, pos) {
92
87
  if (pos->obj) {
93
- rb_gc_mark((VALUE)pos->key);
88
+ rb_gc_mark((VALUE)pos->hash);
94
89
  }
95
90
  }
96
91
  fio_unlock(&lock);
@@ -104,7 +99,7 @@ static void storage_clear(void *ignore) {
104
99
  #endif
105
100
  fio_lock(&lock);
106
101
  fio_hash_free(&storage);
107
- storage = (fio_hash_s)FIO_HASH_INIT;
102
+ storage = (fio_hash_s)FIO_SET_INIT;
108
103
  fio_unlock(&lock);
109
104
  }
110
105
 
@@ -131,7 +126,7 @@ struct IodineStorage_s IodineStore = {
131
126
 
132
127
  /** Initializes the storage unit for first use. */
133
128
  void iodine_storage_init(void) {
134
- fio_hash_new2(&storage, 512);
129
+ fio_hash_capa_require(&storage, 512);
135
130
  VALUE tmp =
136
131
  rb_define_class_under(rb_cObject, "IodineObjectStorage", rb_cData);
137
132
  VALUE storage_obj =
@@ -114,6 +114,10 @@ typedef struct {
114
114
  char const *filename;
115
115
  /** The file name's length. */
116
116
  size_t filename_len;
117
+ /** If data and data_len are set, they will be used as the file's contents. */
118
+ char const *data;
119
+ /** If data and data_len are set, they will be used as the file's contents. */
120
+ size_t data_len;
117
121
  /** Parsing error reporting (can be NULL). */
118
122
  mustache_error_en *err;
119
123
  } mustache_load_args_s;
@@ -551,7 +555,8 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
551
555
  }
552
556
 
553
557
  /* copy the path data (and resolve) into writable memory */
554
- if (args.filename[0] == '~' && args.filename[1] == '/' && getenv("HOME")) {
558
+ if (args.filename && args.filename[0] == '~' && args.filename[1] == '/' &&
559
+ getenv("HOME")) {
555
560
  const char *home = getenv("HOME");
556
561
  path_len = strlen(home);
557
562
  path_capa =
@@ -569,7 +574,6 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
569
574
  args.filename_len += path_len;
570
575
  args.filename = path;
571
576
  }
572
- /* divide faile name from the root path to the file */
573
577
 
574
578
  /*
575
579
  * We need a dynamic array to hold the list of instructions...
@@ -759,7 +763,7 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
759
763
  } \
760
764
  goto error; \
761
765
  } \
762
- if (pread(fd, (data + data_len + 4 + 3 + 3 + path_len), f_data.st_size, \
766
+ if (pread(fd, (data + data_len + 4 + 2 + 4 + path_len), f_data.st_size, \
763
767
  0) != (ssize_t)f_data.st_size) { \
764
768
  if (args.err) { \
765
769
  *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
@@ -794,8 +798,48 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
794
798
  (str) += (step); \
795
799
  }
796
800
 
797
- /* Our first template to load is the root template */
798
- LOAD_TEMPLATE(path, 0, args.filename, args.filename_len);
801
+ if (args.data_len) {
802
+ /* allocate data segment */
803
+ data_len = 4 + 2 + 4 + args.data_len + args.filename_len + 1;
804
+ data = malloc(data_len);
805
+ if (!data) {
806
+ perror("FATAL ERROR: couldn't reallocate memory for mustache "
807
+ "data segment");
808
+ exit(errno);
809
+ }
810
+ /* save instruction position length into template header */
811
+ data[0] = (instructions->head.u.read_only.intruction_count >> 3) & 0xFF;
812
+ data[1] = (instructions->head.u.read_only.intruction_count >> 2) & 0xFF;
813
+ data[2] = (instructions->head.u.read_only.intruction_count >> 1) & 0xFF;
814
+ data[3] = (instructions->head.u.read_only.intruction_count) & 0xFF;
815
+ /* Add section start marker (to support recursion or repeated partials) */
816
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_START);
817
+ /* save filename length */
818
+ data[4 + 0] = (args.filename_len >> 1) & 0xFF;
819
+ data[4 + 1] = args.filename_len & 0xFF;
820
+ /* save data length ("next" pointer) */
821
+ data[4 + 2 + 0] = ((uint32_t)data_len >> 3) & 0xFF;
822
+ data[4 + 2 + 1] = ((uint32_t)data_len >> 2) & 0xFF;
823
+ data[4 + 2 + 2] = ((uint32_t)data_len >> 1) & 0xFF;
824
+ data[4 + 2 + 3] = ((uint32_t)data_len) & 0xFF;
825
+ /* copy filename */
826
+ if (args.filename && args.filename_len)
827
+ memcpy(data + 4 + 2 + 4, args.filename, args.filename_len);
828
+ /* copy data */
829
+ memcpy(data + 4 + 2 + 4 + args.filename_len, args.data, args.data_len);
830
+ ++stack_pos;
831
+ template_stack[stack_pos].data_start = 0;
832
+ template_stack[stack_pos].data_pos = 4 + 3 + 3 + args.filename_len;
833
+ template_stack[stack_pos].data_end = data_len - 1;
834
+ template_stack[stack_pos].delimiter_start = (uint8_t *)"{{";
835
+ template_stack[stack_pos].delimiter_end = (uint8_t *)"}}";
836
+ template_stack[stack_pos].del_start_len = 2;
837
+ template_stack[stack_pos].del_end_len = 2;
838
+ data[data_len - 1] = 0;
839
+ } else {
840
+ /* Our first template to load is the root template */
841
+ LOAD_TEMPLATE(path, 0, args.filename, args.filename_len);
842
+ }
799
843
 
800
844
  /*** As long as the stack has templated to parse - parse the template ***/
801
845
  while (stack_pos) {
@@ -182,8 +182,7 @@ RESP parser callbacks
182
182
  /** a local static callback, called when a parser / protocol error occurs. */
183
183
  static int resp_on_parser_error(resp_parser_s *parser) {
184
184
  struct redis_engine_internal_s *i = parser2data(parser);
185
- FIO_LOG_STATE(
186
- "ERROR: (redis) parser error - attempting to restart connection.\n");
185
+ FIO_LOG_ERROR("(redis) parser error - attempting to restart connection.\n");
187
186
  fio_close(i->uuid);
188
187
  return -1;
189
188
  }
@@ -339,9 +338,8 @@ static void resp_on_pub_message(struct redis_engine_internal_s *i, FIOBJ msg) {
339
338
  fio_unlock(&r->lock);
340
339
  if (!node) {
341
340
  /* TODO: possible ping? from server?! not likely... */
342
- FIO_LOG_STATE(
343
- "WARNING: (redis %d) received a reply when no command was sent.\n",
344
- getpid());
341
+ FIO_LOG_WARNING("(redis %d) received a reply when no command was sent.",
342
+ getpid());
345
343
  return;
346
344
  }
347
345
  node->next = (void *)fiobj_dup(msg);
@@ -360,8 +358,8 @@ static void resp_on_sub_message(struct redis_engine_internal_s *i, FIOBJ msg) {
360
358
  if (FIOBJ_TYPE(msg) != FIOBJ_T_ARRAY) {
361
359
  if (FIOBJ_TYPE(msg) != FIOBJ_T_STRING || fiobj_obj2cstr(msg).len != 4 ||
362
360
  fiobj_obj2cstr(msg).data[0] != 'P') {
363
- FIO_LOG_STATE("WARNING: (redis) unexpected data format in "
364
- "subscription stream:\n");
361
+ FIO_LOG_WARNING("(redis) unexpected data format in "
362
+ "subscription stream:");
365
363
  fio_str_info_s tmp = fiobj_obj2cstr(msg);
366
364
  FIO_LOG_STATE(" %s\n", tmp.data);
367
365
  }
@@ -429,9 +427,9 @@ static void redis_on_close(intptr_t uuid, fio_protocol_s *pr) {
429
427
  if (r->flag) {
430
428
  /* reconnection for subscription connection. */
431
429
  if (uuid != -1) {
432
- FIO_LOG_STATE("WARNING: (redis %d) subscription connection lost. "
433
- "Reconnecting...\n",
434
- (int)getpid());
430
+ FIO_LOG_WARNING("(redis %d) subscription connection lost. "
431
+ "Reconnecting...",
432
+ (int)getpid());
435
433
  }
436
434
  fio_atomic_sub(&r->ref, 1);
437
435
  fio_defer(redis_connect, r, internal);
@@ -441,9 +439,9 @@ static void redis_on_close(intptr_t uuid, fio_protocol_s *pr) {
441
439
  } else {
442
440
  r = pub2redis(pr);
443
441
  if (r->flag && uuid != -1) {
444
- FIO_LOG_STATE("WARNING: (redis %d) publication connection lost. "
445
- "Reconnecting...\n",
446
- (int)getpid());
442
+ FIO_LOG_WARNING("(redis %d) publication connection lost. "
443
+ "Reconnecting...",
444
+ (int)getpid());
447
445
  }
448
446
  r->pub_send = 0;
449
447
  fio_close(r->sub_data.uuid);
@@ -471,8 +469,7 @@ static void redis_sub_ping(intptr_t uuid, fio_protocol_s *pr) {
471
469
  static void redis_pub_ping(intptr_t uuid, fio_protocol_s *pr) {
472
470
  redis_engine_s *r = pub2redis(pr);
473
471
  if (fio_ls_embd_any(&r->queue)) {
474
- FIO_LOG_STATE(
475
- "WARNING: (redis) Redis server unresponsive, disconnecting.\n");
472
+ FIO_LOG_WARNING("(redis) Redis server unresponsive, disconnecting.");
476
473
  fio_close(uuid);
477
474
  return;
478
475
  }
@@ -489,9 +486,9 @@ Connecting to Redis
489
486
  static void redis_on_auth(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
490
487
  if (FIOBJ_TYPE_IS(reply, FIOBJ_T_TRUE)) {
491
488
  fio_str_info_s s = fiobj_obj2cstr(reply);
492
- FIO_LOG_STATE("WARNING: (redis) Authentication FAILED.\n"
493
- " %.*s\n",
494
- (int)s.len, s.data);
489
+ FIO_LOG_WARNING("(redis) Authentication FAILED."
490
+ " %.*s",
491
+ (int)s.len, s.data);
495
492
  }
496
493
  (void)e;
497
494
  (void)udata;
@@ -512,8 +509,8 @@ static void redis_on_connect(intptr_t uuid, void *i_) {
512
509
  if (r->pub_data.uuid == -1) {
513
510
  fio_defer(redis_connect, r, &r->pub_data);
514
511
  }
515
- FIO_LOG_STATE("INFO: (redis %d) subscription connection established.\n",
516
- (int)getpid());
512
+ FIO_LOG_INFO("(redis %d) subscription connection established.",
513
+ (int)getpid());
517
514
  } else {
518
515
  r = pub2redis(i);
519
516
  if (r->auth_len) {
@@ -535,8 +532,8 @@ static void redis_on_connect(intptr_t uuid, void *i_) {
535
532
  }
536
533
  r->pub_send = 1;
537
534
  fio_unlock(&r->lock);
538
- FIO_LOG_STATE("INFO: (redis %d) publication connection established.\n",
539
- (int)getpid());
535
+ FIO_LOG_INFO("(redis %d) publication connection established.",
536
+ (int)getpid());
540
537
  }
541
538
 
542
539
  i->protocol.rsv = 0;
@@ -639,7 +636,7 @@ static void redis_on_publish_root(const fio_pubsub_engine_s *eng,
639
636
  *buf++ = '\r';
640
637
  *buf++ = '\n';
641
638
  *buf = 0;
642
- FIO_LOG_DEBUG("(%d) Publishing:\n%s\n", getpid(), cmd->cmd);
639
+ FIO_LOG_DEBUG("(%d) Publishing:\n%s", getpid(), cmd->cmd);
643
640
  cmd->cmd_len = (uintptr_t)buf - (uintptr_t)(cmd + 1);
644
641
  redis_attach_cmd(r, cmd);
645
642
  return;
@@ -690,7 +687,7 @@ static void redis_on_internal_publish(fio_msg_s *msg) {
690
687
  msg->channel.len -= 8;
691
688
  msg->channel.data += 8;
692
689
  /* forward to publishing */
693
- FIO_LOG_DEBUG("Forwarding to engine %p, on channel %s\n", msg->udata1,
690
+ FIO_LOG_DEBUG("Forwarding to engine %p, on channel %s", msg->udata1,
694
691
  msg->channel.data);
695
692
  redis_on_publish_root(msg->udata1, msg->channel, msg->msg, msg->is_json);
696
693
  }
@@ -706,7 +703,7 @@ static void redis_forward_reply(fio_pubsub_engine_s *e, FIOBJ reply,
706
703
  fio_pubsub_engine_s *engine = (fio_pubsub_engine_s *)fio_str2u64(data + 0);
707
704
  void *callback = (void *)fio_str2u64(data + 8);
708
705
  if (engine != e || !callback) {
709
- FIO_LOG_DEBUG("Redis reply not forwarded (callback: %p)\n", callback);
706
+ FIO_LOG_DEBUG("Redis reply not forwarded (callback: %p)", callback);
710
707
  return;
711
708
  }
712
709
  int32_t pid = (int32_t)fio_str2u32(data + 24);
@@ -740,7 +737,7 @@ static void redis_on_internal_reply(fio_msg_s *msg) {
740
737
  fio_pubsub_engine_s *engine =
741
738
  (fio_pubsub_engine_s *)fio_str2u64(msg->channel.data + 0);
742
739
  if (engine != msg->udata1) {
743
- FIO_LOG_DEBUG("Redis reply not forwarded (engine mismatch: %p != %p)\n",
740
+ FIO_LOG_DEBUG("Redis reply not forwarded (engine mismatch: %p != %p)",
744
741
  (void *)engine, msg->udata1);
745
742
  return;
746
743
  }
@@ -759,8 +756,7 @@ intptr_t redis_engine_send(fio_pubsub_engine_s *engine, FIOBJ command,
759
756
  void *udata),
760
757
  void *udata) {
761
758
  if ((uintptr_t)engine < 4) {
762
- fprintf(stderr, "WARNING: (redis send) trying to use one of the "
763
- "core engines\n");
759
+ FIO_LOG_WARNING("(redis send) trying to use one of the core engines");
764
760
  return -1;
765
761
  }
766
762
  // if(fio_is_master()) {
@@ -830,8 +826,11 @@ static void redis_on_engine_fork(void *r_) {
830
826
  fio_pubsub_engine_s *redis_engine_create
831
827
  FIO_IGNORE_MACRO(struct redis_engine_create_args args) {
832
828
  if (getpid() != fio_parent_pid()) {
833
- fprintf(stderr, "FATAL ERROR: (redis) Redis engine initialization can only "
834
- "be performed in the Root process.\n");
829
+ FIO_LOG_FATAL("(redis) Redis engine initialization can only "
830
+ "be performed in the Root process.");
831
+ kill(0, SIGINT);
832
+ fio_stop();
833
+ return NULL;
835
834
  }
836
835
  if (!args.address.len && args.address.data)
837
836
  args.address.len = strlen(args.address.data);
@@ -911,6 +910,7 @@ FIO_IGNORE_MACRO(struct redis_engine_create_args args) {
911
910
  /* if restarting */
912
911
  fio_state_callback_add(FIO_CALL_PRE_START, redis_on_facil_start, r);
913
912
 
913
+ FIO_LOG_DEBUG("Redis engine initialized %p", (void *)r);
914
914
  return &r->en;
915
915
  }
916
916
 
@@ -925,6 +925,6 @@ void redis_engine_destroy(fio_pubsub_engine_s *engine) {
925
925
  fio_state_callback_remove(FIO_CALL_IN_CHILD, redis_on_engine_fork, r);
926
926
  fio_state_callback_remove(FIO_CALL_ON_SHUTDOWN, redis_on_facil_shutdown, r);
927
927
  fio_state_callback_remove(FIO_CALL_PRE_START, redis_on_facil_start, r);
928
- FIO_LOG_DEBUG("Redis engine destroyed %p\n", (void *)r);
928
+ FIO_LOG_DEBUG("Redis engine destroyed %p", (void *)r);
929
929
  redis_free(r);
930
930
  }
@@ -116,6 +116,7 @@ struct ws_s {
116
116
  size_t max_msg_size;
117
117
  /** active pub/sub subscriptions */
118
118
  fio_ls_s subscriptions;
119
+ fio_lock_i sub_lock;
119
120
  /** socket buffer. */
120
121
  struct buffer_s buffer;
121
122
  /** data length (how much of the buffer actually used). */
@@ -133,9 +134,11 @@ Create/Destroy the websocket subscription objects
133
134
  ***************************************************************************** */
134
135
 
135
136
  static inline void clear_subscriptions(ws_s *ws) {
137
+ fio_lock(&ws->sub_lock);
136
138
  while (fio_ls_any(&ws->subscriptions)) {
137
139
  fio_unsubscribe(fio_ls_pop(&ws->subscriptions));
138
140
  }
141
+ fio_unlock(&ws->sub_lock);
139
142
  }
140
143
 
141
144
  /* *****************************************************************************
@@ -325,7 +328,7 @@ void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
325
328
  websocket_settings_s *args, void *data, size_t length) {
326
329
  ws_s *ws = new_websocket(uuid);
327
330
  if (!ws) {
328
- perror("FATAL ERROR: couldn't allocate Websocket protocol object");
331
+ FIO_LOG_FATAL("couldn't allocate Websocket protocol object");
329
332
  exit(errno);
330
333
  }
331
334
  // we have an active websocket connection - prep the connection buffer
@@ -615,10 +618,7 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
615
618
  if (!args.ws)
616
619
  goto error;
617
620
  websocket_sub_data_s *d = malloc(sizeof(*d));
618
- if (!d) {
619
- websocket_close(args.ws);
620
- goto error;
621
- }
621
+ FIO_ASSERT_ALLOC(d);
622
622
  *d = (websocket_sub_data_s){
623
623
  .udata = args.udata,
624
624
  .on_message = args.on_message,
@@ -640,7 +640,10 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
640
640
  /* don't free `d`, return (`d` freed by callback) */
641
641
  return 0;
642
642
  }
643
+ fio_lock(&args.ws->sub_lock);
643
644
  fio_ls_push(&args.ws->subscriptions, sub);
645
+ fio_unlock(&args.ws->sub_lock);
646
+
644
647
  return (uintptr_t)args.ws->subscriptions.prev;
645
648
  error:
646
649
  if (args.on_unsubscribe)
@@ -653,7 +656,10 @@ error:
653
656
  */
654
657
  void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id) {
655
658
  fio_unsubscribe((subscription_s *)((fio_ls_s *)subscription_id)->obj);
659
+ fio_lock(&ws->sub_lock);
656
660
  fio_ls_remove((fio_ls_s *)subscription_id);
661
+ fio_unlock(&ws->sub_lock);
662
+
657
663
  (void)ws;
658
664
  }
659
665
 
@@ -121,6 +121,18 @@ if(!defined?(after_fork))
121
121
  Iodine.after_fork(*args, &block)
122
122
  end
123
123
  end
124
+ if(!defined?(after_fork_in_worker))
125
+ # Performs a block of code whenever a new worker process spins up (performed once per worker).
126
+ def after_fork_in_worker(*args, &block)
127
+ Iodine.after_fork_in_worker(*args, &block)
128
+ end
129
+ end
130
+ if(!defined?(after_fork_in_master))
131
+ # Performs a block of code whenever a new worker process spins up (performed once per worker).
132
+ def after_fork_in_master(*args, &block)
133
+ Iodine.after_fork_in_master(*args, &block)
134
+ end
135
+ end
124
136
  if(!defined?(on_worker_boot))
125
137
  # Performs a block of code whenever a new worker process spins up (performed once per worker).
126
138
  def on_worker_boot(*args, &block)
@@ -128,7 +140,7 @@ if(!defined?(on_worker_boot))
128
140
  end
129
141
  end
130
142
  if(!defined?(before_fork))
131
- # Performs a block of code just before a new worker process spins up (performed once per worker).
143
+ # Performs a block of code just before a new worker process spins up (performed once per worker, in the master thread).
132
144
  def before_fork(*args, &block)
133
145
  Iodine.before_fork(*args, &block)
134
146
  end