rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -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,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 */
|