isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +44 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,55 @@
1
+ #ifndef H_IODINE_CONNECTION_H
2
+ #define H_IODINE_CONNECTION_H
3
+
4
+ #include "iodine.h"
5
+
6
+ typedef enum {
7
+ IODINE_CONNECTION_RAW,
8
+ IODINE_CONNECTION_WEBSOCKET,
9
+ IODINE_CONNECTION_SSE
10
+ } iodine_connection_type_e;
11
+
12
+ typedef struct {
13
+ iodine_connection_type_e type;
14
+ intptr_t uuid;
15
+ void *arg; /* holds the connection pointer (ws_s / sse_s) */
16
+ VALUE handler;
17
+ VALUE env;
18
+ } iodine_connection_s;
19
+
20
+ /**
21
+ * Creates a new connection object.
22
+ */
23
+ VALUE iodine_connection_new(iodine_connection_s args);
24
+ #define iodine_connection_new(...) \
25
+ iodine_connection_new((iodine_connection_s){__VA_ARGS__})
26
+
27
+ typedef enum {
28
+ IODINE_CONNECTION_ON_OPEN,
29
+ IODINE_CONNECTION_ON_MESSAGE,
30
+ IODINE_CONNECTION_ON_DRAINED,
31
+ IODINE_CONNECTION_PING,
32
+ IODINE_CONNECTION_ON_SHUTDOWN,
33
+ IODINE_CONNECTION_ON_CLOSE
34
+ } iodine_connection_event_type_e;
35
+
36
+ /**
37
+ * Fires a connection object's event. `data` is only for the on_message event.
38
+ */
39
+ void iodine_connection_fire_event(VALUE connection,
40
+ iodine_connection_event_type_e ev,
41
+ VALUE data);
42
+
43
+ /** Initializes the Connection Ruby class. */
44
+ void iodine_connection_init(void);
45
+
46
+ extern const rb_data_type_t iodine_connection_data_type;
47
+
48
+ static inline iodine_connection_s *iodine_connection_CData(VALUE self) {
49
+ iodine_connection_s *c = NULL;
50
+ TypedData_Get_Struct(self, iodine_connection_s, &iodine_connection_data_type,
51
+ c);
52
+ return c;
53
+ }
54
+
55
+ #endif
@@ -0,0 +1,420 @@
1
+ #include "iodine.h"
2
+
3
+ #include <ruby/thread.h>
4
+
5
+ #include <stdint.h>
6
+ // clang-format on
7
+
8
+ #define FIO_INCLUDE_LINKED_LIST
9
+ #include "fio.h"
10
+
11
+ #include <pthread.h>
12
+
13
+ static ID STATE_PRE_START;
14
+ static ID STATE_BEFORE_FORK;
15
+ static ID STATE_AFTER_FORK;
16
+ static ID STATE_ENTER_CHILD;
17
+ static ID STATE_ENTER_MASTER;
18
+ static ID STATE_ON_START;
19
+ static ID STATE_ON_PARENT_CRUSH;
20
+ static ID STATE_ON_CHILD_CRUSH;
21
+ static ID STATE_START_SHUTDOWN;
22
+ static ID STATE_ON_FINISH;
23
+
24
+ /* *****************************************************************************
25
+ IO flushing dedicated thread for protection against blocking code
26
+ ***************************************************************************** */
27
+
28
+ static fio_lock_i sock_io_thread_flag = 0;
29
+ static pthread_t sock_io_pthread;
30
+ typedef struct {
31
+ size_t threads;
32
+ size_t processes;
33
+ } iodine_start_settings_s;
34
+
35
+ static void *iodine_io_thread(void *arg) {
36
+ (void)arg;
37
+ while (sock_io_thread_flag) {
38
+ if (fio_flush_all())
39
+ fio_throttle_thread(500000UL);
40
+ else
41
+ fio_throttle_thread(150000000UL);
42
+ }
43
+ return NULL;
44
+ }
45
+ static void iodine_start_io_thread(void *a_) {
46
+ if (!fio_trylock(&sock_io_thread_flag)) {
47
+ if (pthread_create(&sock_io_pthread, NULL, iodine_io_thread, NULL)) {
48
+ FIO_LOG_ERROR("Couldn't spawn IO thread.");
49
+ };
50
+ FIO_LOG_DEBUG("IO thread started.");
51
+ }
52
+ (void)a_;
53
+ }
54
+
55
+ static void iodine_join_io_thread(void) {
56
+ if (fio_unlock(&sock_io_thread_flag) && sock_io_pthread) {
57
+ pthread_join(sock_io_pthread, NULL);
58
+ sock_io_pthread = (pthread_t)NULL;
59
+ FIO_LOG_DEBUG("IO thread stopped and joined.");
60
+ }
61
+ }
62
+
63
+ /* *****************************************************************************
64
+ The Defer library overriding functions
65
+ ***************************************************************************** */
66
+
67
+ /* used to create Ruby threads and pass them the information they need */
68
+ struct CreateThreadArgs {
69
+ void *(*thread_func)(void *);
70
+ void *arg;
71
+ fio_lock_i lock;
72
+ };
73
+
74
+ /* used for GVL signalling */
75
+ static void call_async_signal(void *pool) {
76
+ fio_stop();
77
+ (void)pool;
78
+ }
79
+
80
+ static void *defer_thread_start(void *args_) {
81
+ struct CreateThreadArgs *args = args_;
82
+ IodineCaller.set_GVL(0);
83
+ args->thread_func(args->arg);
84
+ return NULL;
85
+ }
86
+
87
+ /* the thread's GVL release */
88
+ static VALUE defer_thread_inGVL(void *args_) {
89
+ struct CreateThreadArgs *old_args = args_;
90
+ struct CreateThreadArgs args = *old_args;
91
+ IodineCaller.set_GVL(1);
92
+ fio_unlock(&old_args->lock);
93
+ rb_thread_call_without_gvl(defer_thread_start, &args,
94
+ (void (*)(void *))call_async_signal, args.arg);
95
+ return Qnil;
96
+ }
97
+
98
+ /* Within the GVL, creates a Ruby thread using an API call */
99
+ static void *create_ruby_thread_gvl(void *args) {
100
+ return (void *)IodineStore.add(rb_thread_create(defer_thread_inGVL, args));
101
+ }
102
+
103
+ static void *fork_using_ruby(void *ignr) {
104
+ // stop IO thread, if running (shouldn't occur)
105
+ if (sock_io_pthread) {
106
+ iodine_join_io_thread();
107
+ }
108
+ // fork using Ruby
109
+ const VALUE ProcessClass = rb_const_get(rb_cObject, rb_intern2("Process", 7));
110
+ const VALUE rb_pid = IodineCaller.call(ProcessClass, rb_intern2("fork", 4));
111
+ intptr_t pid = 0;
112
+ if (rb_pid != Qnil) {
113
+ pid = NUM2INT(rb_pid);
114
+ } else {
115
+ pid = 0;
116
+ }
117
+ // manage post forking state for Iodine
118
+ IodineCaller.set_GVL(1); /* enforce GVL state in thread storage */
119
+ if (!pid) {
120
+ IodineStore.after_fork();
121
+ }
122
+ return (void *)pid;
123
+ (void)ignr;
124
+ }
125
+
126
+ /**
127
+ OVERRIDE THIS to replace the default pthread implementation.
128
+ */
129
+ void *fio_thread_new(void *(*thread_func)(void *), void *arg) {
130
+ struct CreateThreadArgs data = (struct CreateThreadArgs){
131
+ .thread_func = thread_func,
132
+ .arg = arg,
133
+ .lock = FIO_LOCK_INIT,
134
+ };
135
+ fio_lock(&data.lock);
136
+ void *thr = IodineCaller.enterGVL(create_ruby_thread_gvl, &data);
137
+ if (!thr || thr == (void *)Qnil || thr == (void *)Qfalse) {
138
+ thr = NULL;
139
+ } else {
140
+ /* wait for thread to signal it's alive. */
141
+ fio_lock(&data.lock);
142
+ }
143
+ return thr;
144
+ }
145
+
146
+ /**
147
+ OVERRIDE THIS to replace the default pthread implementation.
148
+ */
149
+ int fio_thread_join(void *thr) {
150
+ if (!thr || (VALUE)thr == Qfalse || (VALUE)thr == Qnil)
151
+ return -1;
152
+ IodineCaller.call((VALUE)thr, rb_intern("join"));
153
+ IodineStore.remove((VALUE)thr);
154
+ return 0;
155
+ }
156
+
157
+ // void defer_free_thread(void *thr) { (void)thr; }
158
+ void fio_thread_free(void *thr) { IodineStore.remove((VALUE)thr); }
159
+
160
+ /**
161
+ OVERRIDE THIS to replace the default `fork` implementation or to inject hooks
162
+ into the forking function.
163
+
164
+ Behaves like the system's `fork`.
165
+ */
166
+ int fio_fork(void) {
167
+ intptr_t pid = (intptr_t)IodineCaller.enterGVL(fork_using_ruby, NULL);
168
+ return (int)pid;
169
+ }
170
+
171
+ /* *****************************************************************************
172
+ Task performance
173
+ ***************************************************************************** */
174
+
175
+ static ID call_id;
176
+
177
+ static void iodine_defer_performe_once(void *block, void *ignr) {
178
+ IodineCaller.call((VALUE)block, call_id);
179
+ IodineStore.remove((VALUE)block);
180
+ (void)ignr;
181
+ }
182
+
183
+ static void iodine_defer_run_timer(void *block) {
184
+ /* TODO, update return value to allow timer cancellation */
185
+ IodineCaller.call((VALUE)block, call_id);
186
+ }
187
+
188
+ /* *****************************************************************************
189
+ Defer API
190
+ ***************************************************************************** */
191
+
192
+ /**
193
+ * Runs a block of code asyncronously (adds the code to the event queue).
194
+ *
195
+ * Always returns the block of code to executed (Proc object).
196
+ *
197
+ * Code will be executed only while Iodine is running (after {Iodine.start}).
198
+ *
199
+ * Code blocks that where scheduled to run before Iodine enters cluster mode
200
+ * will run on all child processes.
201
+ */
202
+ static VALUE iodine_defer_run(VALUE self) {
203
+ rb_need_block();
204
+ VALUE block = IodineStore.add(rb_block_proc());
205
+ fio_defer(iodine_defer_performe_once, (void *)block, NULL);
206
+ return block;
207
+ (void)self;
208
+ }
209
+
210
+ /**
211
+ Runs the required block after the specified number of milliseconds have passed.
212
+ Time is counted only once Iodine started running (using {Iodine.start}).
213
+
214
+ Tasks scheduled before calling {Iodine.start} will run once for every process.
215
+
216
+ Always returns a copy of the block object.
217
+ */
218
+ static VALUE iodine_defer_run_after(VALUE self, VALUE milliseconds) {
219
+ (void)(self);
220
+ if (milliseconds == Qnil) {
221
+ return iodine_defer_run(self);
222
+ }
223
+ if (TYPE(milliseconds) != T_FIXNUM) {
224
+ rb_raise(rb_eTypeError, "milliseconds must be a number");
225
+ return Qnil;
226
+ }
227
+ size_t milli = FIX2UINT(milliseconds);
228
+ if (milli == 0) {
229
+ return iodine_defer_run(self);
230
+ }
231
+ // requires a block to be passed
232
+ rb_need_block();
233
+ VALUE block = rb_block_proc();
234
+ if (block == Qnil)
235
+ return Qfalse;
236
+ IodineStore.add(block);
237
+ if (fio_run_every(milli, 1, iodine_defer_run_timer, (void *)block,
238
+ (void (*)(void *))IodineStore.remove) == -1) {
239
+ perror("ERROR: Iodine couldn't initialize timer");
240
+ return Qnil;
241
+ }
242
+ return block;
243
+ }
244
+
245
+ // clang-format off
246
+ /**
247
+ Runs the required block after the specified number of milliseconds have passed.
248
+ Time is counted only once Iodine started running (using {Iodine.start}).
249
+
250
+ Accepts:
251
+
252
+ | | |
253
+ |---|---|
254
+ | `:milliseconds` | the number of milliseconds between event repetitions.|
255
+ | `:repetitions` | the number of event repetitions. Defaults to 0 (never ending).|
256
+ | `:block` | (required) a block is required, as otherwise there is nothing to|
257
+ perform.
258
+
259
+ The event will repeat itself until the number of repetitions had been delpeted.
260
+
261
+ Always returns a copy of the block object.
262
+ */
263
+ static VALUE iodine_defer_run_every(int argc, VALUE *argv, VALUE self) {
264
+ // clang-format on
265
+ (void)(self);
266
+ VALUE milliseconds, repetitions, block;
267
+
268
+ rb_scan_args(argc, argv, "11&", &milliseconds, &repetitions, &block);
269
+
270
+ if (TYPE(milliseconds) != T_FIXNUM) {
271
+ rb_raise(rb_eTypeError, "milliseconds must be a number.");
272
+ return Qnil;
273
+ }
274
+ if (repetitions != Qnil && TYPE(repetitions) != T_FIXNUM) {
275
+ rb_raise(rb_eTypeError, "repetitions must be a number or `nil`.");
276
+ return Qnil;
277
+ }
278
+
279
+ size_t milli = FIX2UINT(milliseconds);
280
+ size_t repeat = (repetitions == Qnil) ? 0 : FIX2UINT(repetitions);
281
+ // requires a block to be passed
282
+ rb_need_block();
283
+ IodineStore.add(block);
284
+ if (fio_run_every(milli, repeat, iodine_defer_run_timer, (void *)block,
285
+ (void (*)(void *))IodineStore.remove) == -1) {
286
+ perror("ERROR: Iodine couldn't initialize timer");
287
+ return Qnil;
288
+ }
289
+ return block;
290
+ }
291
+
292
+ /* *****************************************************************************
293
+ Pre/Post `fork`
294
+ ***************************************************************************** */
295
+
296
+ /* performs a Ruby state callback without clearing the Ruby object's memory */
297
+ static void iodine_perform_state_callback_persist(void *blk_) {
298
+ VALUE blk = (VALUE)blk_;
299
+ IodineCaller.call(blk, call_id);
300
+ }
301
+
302
+ // clang-format off
303
+ /**
304
+ Sets a block of code to run when Iodine's core state is updated.
305
+
306
+ @param [Symbol] event the state event for which the block should run (see list).
307
+ @since 0.7.9
308
+
309
+ The state event Symbol can be any of the following:
310
+
311
+ | | |
312
+ |---|---|
313
+ | `:pre_start` | the block will be called once before starting up the IO reactor. |
314
+ | `:before_fork` | the block will be called before each time the IO reactor forks a new worker. |
315
+ | `:after_fork` | the block will be called after each fork (both in parent and workers). |
316
+ | `:enter_child` | the block will be called by a worker process right after forking. |
317
+ | `:enter_master` | the block will be called by the master process after spawning a worker (after forking). |
318
+ | `:on_start` | the block will be called every time a *worker* proceess starts. In single process mode, the master process is also a worker. |
319
+ | `:on_parent_crush` | the block will be called by each worker the moment it detects the master process crashed. |
320
+ | `:on_child_crush` | the block will be called by the parent (master) after a worker process crashed. |
321
+ | `:start_shutdown` | the block will be called before starting the shutdown sequence. |
322
+ | `:on_finish` | the block will be called just before finishing up (both on chlid and parent processes). |
323
+
324
+ Code runs in both the parent and the child.
325
+ */
326
+ static VALUE iodine_on_state(VALUE self, VALUE event) {
327
+ // clang-format on
328
+ rb_need_block();
329
+ Check_Type(event, T_SYMBOL);
330
+ VALUE block = rb_block_proc();
331
+ IodineStore.add(block);
332
+ ID state = rb_sym2id(event);
333
+
334
+ if (state == STATE_PRE_START) {
335
+ fio_state_callback_add(FIO_CALL_PRE_START,
336
+ iodine_perform_state_callback_persist,
337
+ (void *)block);
338
+ } else if (state == STATE_BEFORE_FORK) {
339
+ fio_state_callback_add(FIO_CALL_BEFORE_FORK,
340
+ iodine_perform_state_callback_persist,
341
+ (void *)block);
342
+ } else if (state == STATE_AFTER_FORK) {
343
+ fio_state_callback_add(FIO_CALL_AFTER_FORK,
344
+ iodine_perform_state_callback_persist,
345
+ (void *)block);
346
+ } else if (state == STATE_ENTER_CHILD) {
347
+ fio_state_callback_add(FIO_CALL_IN_CHILD,
348
+ iodine_perform_state_callback_persist,
349
+ (void *)block);
350
+ } else if (state == STATE_ENTER_MASTER) {
351
+ fio_state_callback_add(FIO_CALL_IN_MASTER,
352
+ iodine_perform_state_callback_persist,
353
+ (void *)block);
354
+ } else if (state == STATE_ON_START) {
355
+ fio_state_callback_add(FIO_CALL_ON_START,
356
+ iodine_perform_state_callback_persist,
357
+ (void *)block);
358
+ } else if (state == STATE_ON_PARENT_CRUSH) {
359
+ fio_state_callback_add(FIO_CALL_ON_PARENT_CRUSH,
360
+ iodine_perform_state_callback_persist,
361
+ (void *)block);
362
+ } else if (state == STATE_ON_CHILD_CRUSH) {
363
+ fio_state_callback_add(FIO_CALL_ON_CHILD_CRUSH,
364
+ iodine_perform_state_callback_persist,
365
+ (void *)block);
366
+ } else if (state == STATE_START_SHUTDOWN) {
367
+ fio_state_callback_add(FIO_CALL_ON_SHUTDOWN,
368
+ iodine_perform_state_callback_persist,
369
+ (void *)block);
370
+ } else if (state == STATE_ON_FINISH) {
371
+ fio_state_callback_add(FIO_CALL_ON_FINISH,
372
+ iodine_perform_state_callback_persist,
373
+ (void *)block);
374
+ } else {
375
+ IodineStore.remove(block);
376
+ rb_raise(rb_eTypeError, "unknown event in Iodine.on_state");
377
+ }
378
+ return block;
379
+ (void)self;
380
+ }
381
+
382
+ /* Performs any cleanup before worker dies */
383
+ static void iodine_defer_on_finish(void *ignr) {
384
+ (void)ignr;
385
+ iodine_join_io_thread();
386
+ }
387
+
388
+ /* *****************************************************************************
389
+ Add defer API to Iodine
390
+ ***************************************************************************** */
391
+
392
+ void iodine_defer_initialize(void) {
393
+ call_id = rb_intern2("call", 4);
394
+ rb_define_module_function(IodineModule, "run", iodine_defer_run, 0);
395
+ rb_define_module_function(IodineModule, "defer", iodine_defer_run, 0);
396
+
397
+ rb_define_module_function(IodineModule, "run_after", iodine_defer_run_after,
398
+ 1);
399
+ rb_define_module_function(IodineModule, "run_every", iodine_defer_run_every,
400
+ -1);
401
+ rb_define_module_function(IodineModule, "on_state", iodine_on_state, 1);
402
+
403
+ STATE_PRE_START = rb_intern("pre_start");
404
+ STATE_BEFORE_FORK = rb_intern("before_fork");
405
+ STATE_AFTER_FORK = rb_intern("after_fork");
406
+ STATE_ENTER_CHILD = rb_intern("enter_child");
407
+ STATE_ENTER_MASTER = rb_intern("enter_master");
408
+ STATE_ON_START = rb_intern("on_start");
409
+ STATE_ON_PARENT_CRUSH = rb_intern("on_parent_crush");
410
+ STATE_ON_CHILD_CRUSH = rb_intern("on_child_crush");
411
+ STATE_START_SHUTDOWN = rb_intern("start_shutdown");
412
+ STATE_ON_FINISH = rb_intern("on_finish");
413
+
414
+ /* start the IO thread is workrs (only starts in root if root is worker) */
415
+ fio_state_callback_add(FIO_CALL_ON_START, iodine_start_io_thread, NULL);
416
+ /* stop the IO thread before exit */
417
+ fio_state_callback_add(FIO_CALL_ON_FINISH, iodine_defer_on_finish, NULL);
418
+ /* kill IO thread even after a non-graceful iodine shutdown (force-quit) */
419
+ fio_state_callback_add(FIO_CALL_AT_EXIT, iodine_defer_on_finish, NULL);
420
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef H_IODINE_DEFER_H
2
+ #define H_IODINE_DEFER_H
3
+
4
+ void iodine_defer_initialize(void);
5
+
6
+ #endif
@@ -0,0 +1,120 @@
1
+ #ifndef H_RB_FIOBJ2RUBY_H
2
+ /*
3
+ Copyright: Boaz segev, 2016-2017
4
+ License: MIT
5
+
6
+ Feel free to copy, use and enjoy according to the license provided.
7
+ */
8
+ #define H_RB_FIOBJ2RUBY_H
9
+ #include <fiobj.h>
10
+ #include <ruby.h>
11
+
12
+ #include "iodine_store.h"
13
+
14
+ typedef struct {
15
+ FIOBJ stack;
16
+ uintptr_t count;
17
+ VALUE rb;
18
+ uint8_t str2sym;
19
+ } fiobj2rb_s;
20
+
21
+ typedef struct {
22
+ uint8_t str2sym;
23
+ } fiobj2rb_settings_s;
24
+
25
+ static inline VALUE fiobj2rb(FIOBJ o, uint8_t str2sym) {
26
+ VALUE rb;
27
+ if (!o)
28
+ return Qnil;
29
+ switch (FIOBJ_TYPE(o)) {
30
+ case FIOBJ_T_NUMBER:
31
+ rb = LONG2FIX(fiobj_obj2num(o));
32
+ break;
33
+ case FIOBJ_T_TRUE:
34
+ rb = Qtrue;
35
+ break;
36
+ case FIOBJ_T_FALSE:
37
+ rb = Qfalse;
38
+ break;
39
+ case FIOBJ_T_FLOAT:
40
+ rb = rb_float_new(fiobj_obj2float(o));
41
+ break;
42
+ case FIOBJ_T_DATA: /* fallthrough */
43
+ case FIOBJ_T_UNKNOWN: /* fallthrough */
44
+ case FIOBJ_T_STRING: {
45
+ fio_str_info_s tmp = fiobj_obj2cstr(o);
46
+ if (str2sym) {
47
+ rb = rb_intern2(tmp.data, tmp.len);
48
+ rb = ID2SYM(rb);
49
+ } else {
50
+ rb = rb_str_new(tmp.data, tmp.len);
51
+ }
52
+
53
+ } break;
54
+ case FIOBJ_T_ARRAY:
55
+ rb = rb_ary_new();
56
+ break;
57
+ case FIOBJ_T_HASH:
58
+ rb = rb_hash_new();
59
+ break;
60
+ case FIOBJ_T_NULL: /* fallthrough */
61
+ default:
62
+ rb = Qnil;
63
+ break;
64
+ };
65
+ return rb;
66
+ }
67
+
68
+ static int fiobj2rb_task(FIOBJ o, void *data_) {
69
+ fiobj2rb_s *data = data_;
70
+ VALUE rb_tmp;
71
+ rb_tmp = fiobj2rb(o, 0);
72
+ IodineStore.add(rb_tmp);
73
+ if (data->rb) {
74
+ if (RB_TYPE_P(data->rb, T_HASH)) {
75
+ rb_hash_aset(data->rb, fiobj2rb(fiobj_hash_key_in_loop(), data->str2sym),
76
+ rb_tmp);
77
+ } else {
78
+ rb_ary_push(data->rb, rb_tmp);
79
+ }
80
+ --(data->count);
81
+ IodineStore.remove(rb_tmp);
82
+ } else {
83
+ data->rb = rb_tmp;
84
+ // IodineStore.add(rb_tmp);
85
+ }
86
+ if (FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY)) {
87
+ fiobj_ary_push(data->stack, (FIOBJ)data->count);
88
+ fiobj_ary_push(data->stack, (FIOBJ)data->rb);
89
+ data->count = fiobj_ary_count(o);
90
+ data->rb = rb_tmp;
91
+ } else if (FIOBJ_TYPE_IS(o, FIOBJ_T_HASH)) {
92
+ fiobj_ary_push(data->stack, (FIOBJ)data->count);
93
+ fiobj_ary_push(data->stack, (FIOBJ)data->rb);
94
+ data->count = fiobj_hash_count(o);
95
+ data->rb = rb_tmp;
96
+ }
97
+ while (data->count == 0 && fiobj_ary_count(data->stack)) {
98
+ data->rb = fiobj_ary_pop(data->stack);
99
+ data->count = fiobj_ary_pop(data->stack);
100
+ }
101
+ return 0;
102
+ }
103
+
104
+ static inline VALUE fiobj2rb_deep(FIOBJ obj, uint8_t str2sym) {
105
+ fiobj2rb_s data = {.stack = fiobj_ary_new2(4), .str2sym = str2sym};
106
+
107
+ /* deep copy */
108
+ fiobj_each2(obj, fiobj2rb_task, &data);
109
+ /* cleanup (shouldn't happen, but what the hell)... */
110
+ while (fiobj_ary_pop(data.stack))
111
+ ;
112
+ fiobj_free(data.stack);
113
+ // IodineStore.remove(data.rb); // don't remove data
114
+ return data.rb;
115
+ }
116
+
117
+ // require 'iodine'
118
+ // Iodine::JSON.parse "{\"1\":[1,2,3,4]}"
119
+ // Iodine::JSON.parse IO.binread("")
120
+ #endif /* H_RB_FIOBJ2RUBY_H */