iodine 0.4.19 → 0.5.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/pubsub.h
CHANGED
@@ -6,19 +6,21 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
6
6
|
*/
|
7
7
|
#ifndef H_FACIL_PUBSUB_H
|
8
8
|
/**
|
9
|
-
This pub/sub API is designed to unload pub/sub stress from external messanging
|
10
|
-
systems onto the local process.
|
11
|
-
|
12
|
-
For example, the NULL pub/sub engine, which is routed to the facil_cluster
|
13
|
-
engine, will only publish a single message per process instead of a message
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
* This pub/sub API is designed to unload pub/sub stress from external messanging
|
10
|
+
* systems onto the local process.
|
11
|
+
|
12
|
+
* For example, the NULL pub/sub engine, which is routed to the facil_cluster
|
13
|
+
* engine, will only publish a single message per process instead of a message
|
14
|
+
per
|
15
|
+
* client, allowing the cluster communication channel to be less crowded when
|
16
|
+
* possible.
|
17
|
+
*
|
18
|
+
* This should allow pub/sub engines, such as Redis, to spread their workload
|
19
|
+
* between all of an application's processes, enhancing overall performance.
|
19
20
|
*/
|
20
21
|
#define H_FACIL_PUBSUB_H
|
21
22
|
#include "facil.h"
|
23
|
+
#include "fiobj.h"
|
22
24
|
|
23
25
|
/* support C++ */
|
24
26
|
#ifdef __cplusplus
|
@@ -35,22 +37,31 @@ typedef struct pubsub_sub_s *pubsub_sub_pt;
|
|
35
37
|
/** A pub/sub engine data structure. See details later on. */
|
36
38
|
typedef struct pubsub_engine_s pubsub_engine_s;
|
37
39
|
|
38
|
-
/** The
|
40
|
+
/** The default pub/sub engine.
|
41
|
+
* This engine performs pub/sub within a group of processes (process cluster).
|
42
|
+
*
|
43
|
+
* The process cluser is initialized by the `facil_run` command with `processes`
|
44
|
+
* set to more than 1.
|
45
|
+
*/
|
46
|
+
extern pubsub_engine_s const *PUBSUB_CLUSTER_ENGINE;
|
47
|
+
|
48
|
+
/** An engine that performs pub/sub only within a single process. */
|
49
|
+
extern pubsub_engine_s const *PUBSUB_PROCESS_ENGINE;
|
50
|
+
|
51
|
+
/** Allows process wide changes to the default Pub/Sub Engine.
|
52
|
+
* Setting a new default before calling `facil_run` will change the default for
|
53
|
+
* the whole process cluster.
|
54
|
+
*/
|
55
|
+
extern pubsub_engine_s *PUBSUB_DEFAULT_ENGINE;
|
56
|
+
|
57
|
+
/** Publishing and on_message callback arguments. */
|
39
58
|
typedef struct pubsub_message_s {
|
40
|
-
/** The pub/sub engine
|
59
|
+
/** The pub/sub engine that should be used to farward this message. */
|
41
60
|
pubsub_engine_s const *engine;
|
42
61
|
/** The pub/sub target channnel. */
|
43
|
-
|
44
|
-
char *name;
|
45
|
-
uint32_t len;
|
46
|
-
} channel;
|
62
|
+
FIOBJ channel;
|
47
63
|
/** The pub/sub message. */
|
48
|
-
|
49
|
-
char *data;
|
50
|
-
uint32_t len;
|
51
|
-
} msg;
|
52
|
-
/** indicates that pattern matching was used. */
|
53
|
-
unsigned use_pattern : 1;
|
64
|
+
FIOBJ message;
|
54
65
|
/** The subscription that prompted the message to be routed to the client. */
|
55
66
|
pubsub_sub_pt subscription;
|
56
67
|
/** Client opaque data pointer (from the `subscribe`) function call. */
|
@@ -61,13 +72,8 @@ typedef struct pubsub_message_s {
|
|
61
72
|
|
62
73
|
/** The arguments used for `pubsub_subscribe` or `pubsub_find_sub`. */
|
63
74
|
struct pubsub_subscribe_args {
|
64
|
-
/** The
|
65
|
-
|
66
|
-
/** The channel to subscribe to. */
|
67
|
-
struct {
|
68
|
-
char *name;
|
69
|
-
uint32_t len;
|
70
|
-
} channel;
|
75
|
+
/** The channel namr used for the subscription. */
|
76
|
+
FIOBJ channel;
|
71
77
|
/** The on message callback. the `*msg` pointer is to a temporary object. */
|
72
78
|
void (*on_message)(pubsub_message_s *msg);
|
73
79
|
/** An optional callback for when a subscription is fully canceled. */
|
@@ -80,34 +86,12 @@ struct pubsub_subscribe_args {
|
|
80
86
|
unsigned use_pattern : 1;
|
81
87
|
};
|
82
88
|
|
83
|
-
/** The arguments used for `pubsub_publish`. */
|
84
|
-
struct pubsub_publish_args {
|
85
|
-
/** The pub/sub engine to use. NULL defaults to the local cluster engine. */
|
86
|
-
pubsub_engine_s const *engine;
|
87
|
-
/** The channel to publish to. */
|
88
|
-
struct {
|
89
|
-
char *name;
|
90
|
-
uint32_t len;
|
91
|
-
} channel;
|
92
|
-
/** The data being pushed. */
|
93
|
-
struct {
|
94
|
-
char *data;
|
95
|
-
uint32_t len;
|
96
|
-
} msg;
|
97
|
-
/** Use pattern matching for channel publication. */
|
98
|
-
unsigned use_pattern : 1;
|
99
|
-
/**
|
100
|
-
* Push the message to the whole cluster, using the cluster engine.
|
101
|
-
* Always TRUE unless an engine was specified.
|
102
|
-
*/
|
103
|
-
unsigned push2cluster : 1;
|
104
|
-
};
|
105
|
-
|
106
89
|
/**
|
107
90
|
* Subscribes to a specific channel.
|
108
91
|
*
|
109
92
|
* Returns a subscription pointer or NULL (failure).
|
110
|
-
*/
|
93
|
+
*/
|
94
|
+
pubsub_sub_pt pubsub_subscribe(struct pubsub_subscribe_args);
|
111
95
|
#define pubsub_subscribe(...) \
|
112
96
|
pubsub_subscribe((struct pubsub_subscribe_args){__VA_ARGS__})
|
113
97
|
|
@@ -125,20 +109,28 @@ pubsub_sub_pt pubsub_find_sub(struct pubsub_subscribe_args);
|
|
125
109
|
pubsub_find_sub((struct pubsub_subscribe_args){__VA_ARGS__})
|
126
110
|
|
127
111
|
/**
|
128
|
-
* Unsubscribes from a specific
|
112
|
+
* Unsubscribes from a specific subscription.
|
113
|
+
*
|
114
|
+
* Note: This should be called exactly the same number times as
|
115
|
+
* `pubsub_subscribe`. Any less and a memory leak might occur. Any more, and the
|
116
|
+
* program might crash.
|
129
117
|
*
|
130
118
|
* Returns 0 on success and -1 on failure.
|
131
119
|
*/
|
132
|
-
|
120
|
+
int pubsub_unsubscribe(pubsub_sub_pt subscription);
|
133
121
|
|
134
122
|
/**
|
135
123
|
* Publishes a message to a channel belonging to a pub/sub service (engine).
|
136
124
|
*
|
137
|
-
* Returns 0 on success and -1 on failure.
|
125
|
+
* Returns 0 on success and -1 on failure (i.e., no channel, no message or no
|
126
|
+
* known subscriptions).
|
127
|
+
*
|
128
|
+
* NOTE: Memory ownership is retained by the calling function. Both the channel
|
129
|
+
* and the message should be freed when the caller is done with them.
|
138
130
|
*/
|
139
|
-
int pubsub_publish(struct
|
131
|
+
int pubsub_publish(struct pubsub_message_s);
|
140
132
|
#define pubsub_publish(...) \
|
141
|
-
pubsub_publish((struct
|
133
|
+
pubsub_publish((struct pubsub_message_s){__VA_ARGS__})
|
142
134
|
|
143
135
|
/**
|
144
136
|
* defers message hadling if it can't be performed (i.e., resource is busy) or
|
@@ -160,59 +152,49 @@ void pubsub_defer(pubsub_message_s *msg);
|
|
160
152
|
/**
|
161
153
|
* Pub/Sub services (engines) MUST provide the listed function pointers.
|
162
154
|
*
|
155
|
+
* Engines should also register using the `pubsub_engine_register` function.
|
156
|
+
*
|
157
|
+
* Engines should deregister, before being destroyed, by using the
|
158
|
+
* `pubsub_engine_deregister` function.
|
159
|
+
*
|
163
160
|
* When an engine received a message to publish, they should call the
|
164
|
-
* `
|
165
|
-
*
|
166
|
-
*
|
167
|
-
*
|
168
|
-
* .
|
169
|
-
* .channel
|
170
|
-
* .
|
171
|
-
*
|
172
|
-
*
|
173
|
-
*
|
174
|
-
*
|
175
|
-
* Engines MUST survive until the pub/sub service is finished using them and
|
176
|
-
* there are no more subscriptions.
|
161
|
+
* `pubsub_publish` function with the engine to which the message is forwarded.
|
162
|
+
* i.e.:
|
163
|
+
*
|
164
|
+
* pubsub_publish(
|
165
|
+
* .engine = PUBSUB_PROCESS_ENGINE,
|
166
|
+
* .channel = channel_name,
|
167
|
+
* .message = msg_body );
|
168
|
+
*
|
169
|
+
* Engines MUST NOT free any of the FIOBJ objects they receive.
|
170
|
+
*
|
177
171
|
*/
|
178
172
|
struct pubsub_engine_s {
|
179
|
-
/**
|
180
|
-
|
181
|
-
|
182
|
-
/**
|
183
|
-
void (*unsubscribe)(const pubsub_engine_s *eng,
|
173
|
+
/** Must subscribe channel. Failures are ignored. */
|
174
|
+
void (*subscribe)(const pubsub_engine_s *eng, FIOBJ channel,
|
175
|
+
uint8_t use_pattern);
|
176
|
+
/** Must unsubscribe channel. Failures are ignored. */
|
177
|
+
void (*unsubscribe)(const pubsub_engine_s *eng, FIOBJ channel,
|
184
178
|
uint8_t use_pattern);
|
185
179
|
/** Should return 0 on success and -1 on failure. */
|
186
|
-
int (*publish)(const pubsub_engine_s *eng,
|
187
|
-
|
188
|
-
|
189
|
-
|
180
|
+
int (*publish)(const pubsub_engine_s *eng, FIOBJ channel, FIOBJ msg);
|
181
|
+
/**
|
182
|
+
* facil.io will call this callback whenever starting, or restarting, the
|
183
|
+
* reactor.
|
184
|
+
*
|
185
|
+
* This will be called when facil.io starts (the master process).
|
186
|
+
*
|
187
|
+
* This will also be called when forking, after facil.io closes all
|
188
|
+
* connections and claim to shut down (running all deferred event).
|
189
|
+
*/
|
190
|
+
void (*on_startup)(const pubsub_engine_s *eng);
|
190
191
|
};
|
191
192
|
|
192
|
-
/**
|
193
|
-
*
|
194
|
-
*
|
195
|
-
* The process cluser is initialized by the `facil_run` command with `processes`
|
196
|
-
* set to more than 1.
|
197
|
-
*/
|
198
|
-
extern const pubsub_engine_s *PUBSUB_CLUSTER_ENGINE;
|
199
|
-
|
200
|
-
/** An engine that performs pub/sub only within a single process. */
|
201
|
-
extern const pubsub_engine_s *PUBSUB_PROCESS_ENGINE;
|
202
|
-
|
203
|
-
/** Allows process wide changes to the default Pub/Sub Engine.
|
204
|
-
* Setting a new default before calling `facil_run` will change the default for
|
205
|
-
* the whole process cluster.
|
206
|
-
*/
|
207
|
-
extern const pubsub_engine_s *PUBSUB_DEFAULT_ENGINE;
|
193
|
+
/** Registers an engine, so it's callback can be called. */
|
194
|
+
void pubsub_engine_register(pubsub_engine_s *engine);
|
208
195
|
|
209
|
-
/**
|
210
|
-
*
|
211
|
-
* The `udata*` and `subscription` fields are ignored.
|
212
|
-
*/
|
213
|
-
void pubsub_engine_distribute(pubsub_message_s msg);
|
214
|
-
#define pubsub_engine_distribute(...) \
|
215
|
-
pubsub_engine_distribute((pubsub_message_s){__VA_ARGS__})
|
196
|
+
/** Unregisters an engine, so it could be safely destroyed. */
|
197
|
+
void pubsub_engine_deregister(pubsub_engine_s *engine);
|
216
198
|
|
217
199
|
/**
|
218
200
|
* Engines can ask facil.io to resubscribe to all active channels.
|
data/ext/iodine/rb-call.c
CHANGED
@@ -23,7 +23,6 @@ typedef struct {
|
|
23
23
|
VALUE obj;
|
24
24
|
int argc;
|
25
25
|
VALUE *argv;
|
26
|
-
VALUE returned;
|
27
26
|
ID method;
|
28
27
|
int exception;
|
29
28
|
} iodine_rb_task_s;
|
@@ -70,7 +69,7 @@ static void *handle_exception(void *ignr) {
|
|
70
69
|
(int)RSTRING_LEN(msg), RSTRING_PTR(msg));
|
71
70
|
}
|
72
71
|
rb_backtrace();
|
73
|
-
fprintf(stderr, "\n");
|
72
|
+
fprintf(stderr, "\n\n");
|
74
73
|
rb_set_errinfo(Qnil);
|
75
74
|
}
|
76
75
|
return (void *)Qnil;
|
@@ -130,8 +129,9 @@ static VALUE iodin_rb_call_arg(VALUE obj, ID method, int argc, VALUE *argv) {
|
|
130
129
|
.argc = argc,
|
131
130
|
.argv = argv};
|
132
131
|
void *ret;
|
133
|
-
if (in_gvl)
|
134
|
-
return (VALUE)
|
132
|
+
if (in_gvl) {
|
133
|
+
return (VALUE)iodine_protected_call(&task);
|
134
|
+
}
|
135
135
|
in_gvl = 1;
|
136
136
|
ret = rb_thread_call_with_gvl(iodine_protected_call, &task);
|
137
137
|
in_gvl = 0;
|
data/ext/iodine/rb-defer.c
CHANGED
@@ -6,14 +6,19 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
6
6
|
*/
|
7
7
|
// clang-format off
|
8
8
|
#include "rb-registry.h"
|
9
|
+
#include "rb-call.h"
|
10
|
+
#include "iodine.h"
|
9
11
|
#include <ruby.h>
|
10
12
|
#include <ruby/thread.h>
|
11
13
|
|
12
14
|
#include <stdint.h>
|
13
15
|
// clang-format on
|
14
16
|
|
17
|
+
#include <spnlock.inc>
|
15
18
|
#include "defer.h"
|
16
19
|
|
20
|
+
#include <pthread.h>
|
21
|
+
|
17
22
|
/* *****************************************************************************
|
18
23
|
Local helpers
|
19
24
|
***************************************************************************** */
|
@@ -26,33 +31,46 @@ struct CreateThreadArgs {
|
|
26
31
|
/* used here but declared elsewhere */
|
27
32
|
void call_async_signal(void *pool) { defer_pool_stop((pool_pt)pool); }
|
28
33
|
|
29
|
-
|
30
|
-
static VALUE thread_loop(void *args_) {
|
34
|
+
static void *defer_thread_start(void *args_) {
|
31
35
|
struct CreateThreadArgs *args = args_;
|
32
36
|
void *(*thread_func)(void *) = args->thread_func;
|
33
37
|
void *arg = args->arg;
|
34
|
-
free(
|
35
|
-
|
36
|
-
|
38
|
+
free(args);
|
39
|
+
RubyCaller.set_gvl_state(0);
|
40
|
+
thread_func(arg);
|
41
|
+
return NULL;
|
42
|
+
}
|
43
|
+
|
44
|
+
/* the thread's GVL release */
|
45
|
+
static VALUE defer_thread_inGVL(void *args_) {
|
46
|
+
struct CreateThreadArgs *args = args_;
|
47
|
+
rb_thread_call_without_gvl(defer_thread_start, args_,
|
48
|
+
(void (*)(void *))call_async_signal, args->arg);
|
37
49
|
return Qnil;
|
38
50
|
}
|
39
51
|
|
40
52
|
/* Within the GVL, creates a Ruby thread using an API call */
|
41
53
|
static void *create_ruby_thread_gvl(void *args) {
|
42
|
-
return (void *)Registry.add(rb_thread_create(
|
43
|
-
}
|
44
|
-
|
45
|
-
/* protect the call to join from any exceptions */
|
46
|
-
static void *inner_join_with_rbthread_(void *rbt) {
|
47
|
-
return (void *)rb_funcall((VALUE)rbt, rb_intern("join"), 0);
|
54
|
+
return (void *)Registry.add(rb_thread_create(defer_thread_inGVL, args));
|
48
55
|
}
|
49
56
|
|
50
57
|
static void *fork_using_ruby(void *ignr) {
|
58
|
+
RubyCaller.call(Iodine, rb_intern("before_fork"));
|
51
59
|
const VALUE ProcessClass = rb_const_get(rb_cObject, rb_intern("Process"));
|
52
|
-
const VALUE
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
const VALUE rb_pid = RubyCaller.call(ProcessClass, rb_intern("fork"));
|
61
|
+
intptr_t pid = 0;
|
62
|
+
if (rb_pid != Qnil) {
|
63
|
+
pid = NUM2INT(rb_pid);
|
64
|
+
} else {
|
65
|
+
pid = 0;
|
66
|
+
}
|
67
|
+
RubyCaller.set_gvl_state(1); /* enforce GVL state in thread storage */
|
68
|
+
if (!pid) {
|
69
|
+
Registry.on_fork();
|
70
|
+
RubyCaller.call(Iodine, rb_intern("after_fork"));
|
71
|
+
}
|
72
|
+
return (void *)pid;
|
73
|
+
(void)ignr;
|
56
74
|
}
|
57
75
|
|
58
76
|
/* *****************************************************************************
|
@@ -62,14 +80,17 @@ The Defer library overriding functions
|
|
62
80
|
/**
|
63
81
|
OVERRIDE THIS to replace the default pthread implementation.
|
64
82
|
*/
|
65
|
-
void *defer_new_thread(void *(*thread_func)(void *),
|
83
|
+
void *defer_new_thread(void *(*thread_func)(void *), void *arg) {
|
66
84
|
struct CreateThreadArgs *data = malloc(sizeof(*data));
|
67
85
|
if (!data)
|
68
86
|
return NULL;
|
69
|
-
*data = (struct CreateThreadArgs){
|
70
|
-
|
71
|
-
|
87
|
+
*data = (struct CreateThreadArgs){
|
88
|
+
.thread_func = thread_func, .arg = arg,
|
89
|
+
};
|
90
|
+
void *thr = RubyCaller.call_c(create_ruby_thread_gvl, data);
|
91
|
+
if (!thr || thr == (void *)Qnil || thr == (void *)Qfalse) {
|
72
92
|
thr = NULL;
|
93
|
+
}
|
73
94
|
return thr;
|
74
95
|
}
|
75
96
|
|
@@ -79,18 +100,21 @@ OVERRIDE THIS to replace the default pthread implementation.
|
|
79
100
|
int defer_join_thread(void *thr) {
|
80
101
|
if (!thr || (VALUE)thr == Qfalse || (VALUE)thr == Qnil)
|
81
102
|
return -1;
|
82
|
-
|
103
|
+
RubyCaller.call((VALUE)thr, rb_intern("join"));
|
83
104
|
Registry.remove((VALUE)thr);
|
84
105
|
return 0;
|
85
106
|
}
|
86
107
|
|
108
|
+
// void defer_free_thread(void *thr) { (void)thr; }
|
109
|
+
void defer_free_thread(void *thr) { Registry.remove((VALUE)thr); }
|
110
|
+
|
87
111
|
/**
|
88
112
|
OVERRIDE THIS to replace the default `fork` implementation or to inject hooks
|
89
113
|
into the forking function.
|
90
114
|
|
91
115
|
Behaves like the system's `fork`.
|
92
116
|
*/
|
93
|
-
int
|
94
|
-
intptr_t pid = (intptr_t)
|
117
|
+
int facil_fork(void) {
|
118
|
+
intptr_t pid = (intptr_t)RubyCaller.call_c(fork_using_ruby, NULL);
|
95
119
|
return (int)pid;
|
96
120
|
}
|
@@ -0,0 +1,117 @@
|
|
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 "rb-registry.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 { uint8_t str2sym; } fiobj2rb_settings_s;
|
22
|
+
|
23
|
+
static inline VALUE fiobj2rb(FIOBJ o, uint8_t str2sym) {
|
24
|
+
VALUE rb;
|
25
|
+
if (!o)
|
26
|
+
return Qnil;
|
27
|
+
switch (FIOBJ_TYPE(o)) {
|
28
|
+
case FIOBJ_T_NUMBER:
|
29
|
+
rb = LONG2FIX(fiobj_obj2num(o));
|
30
|
+
break;
|
31
|
+
case FIOBJ_T_NULL:
|
32
|
+
rb = Qnil;
|
33
|
+
break;
|
34
|
+
case FIOBJ_T_TRUE:
|
35
|
+
rb = Qtrue;
|
36
|
+
break;
|
37
|
+
case FIOBJ_T_FALSE:
|
38
|
+
rb = Qfalse;
|
39
|
+
break;
|
40
|
+
case FIOBJ_T_FLOAT:
|
41
|
+
rb = DBL2NUM(fiobj_obj2float(o));
|
42
|
+
break;
|
43
|
+
case FIOBJ_T_DATA: /* fallthrough */
|
44
|
+
case FIOBJ_T_UNKNOWN: /* fallthrough */
|
45
|
+
case FIOBJ_T_STRING: {
|
46
|
+
fio_cstr_s tmp = fiobj_obj2cstr(o);
|
47
|
+
if (str2sym) {
|
48
|
+
rb = rb_intern2(tmp.data, tmp.len);
|
49
|
+
rb = ID2SYM(rb);
|
50
|
+
} else {
|
51
|
+
rb = rb_str_new(tmp.data, tmp.len);
|
52
|
+
}
|
53
|
+
|
54
|
+
} break;
|
55
|
+
case FIOBJ_T_ARRAY:
|
56
|
+
rb = rb_ary_new();
|
57
|
+
break;
|
58
|
+
case FIOBJ_T_HASH:
|
59
|
+
rb = rb_hash_new();
|
60
|
+
break;
|
61
|
+
};
|
62
|
+
return rb;
|
63
|
+
}
|
64
|
+
|
65
|
+
static int fiobj2rb_task(FIOBJ o, void *data_) {
|
66
|
+
fiobj2rb_s *data = data_;
|
67
|
+
VALUE rb_tmp;
|
68
|
+
rb_tmp = fiobj2rb(o, 0);
|
69
|
+
Registry.add(rb_tmp);
|
70
|
+
if (data->rb) {
|
71
|
+
if (RB_TYPE_P(data->rb, T_HASH)) {
|
72
|
+
rb_hash_aset(data->rb, fiobj2rb(fiobj_hash_key_in_loop(), data->str2sym),
|
73
|
+
rb_tmp);
|
74
|
+
} else {
|
75
|
+
rb_ary_push(data->rb, rb_tmp);
|
76
|
+
}
|
77
|
+
--(data->count);
|
78
|
+
Registry.remove(rb_tmp);
|
79
|
+
} else {
|
80
|
+
data->rb = rb_tmp;
|
81
|
+
// Registry.add(rb_tmp);
|
82
|
+
}
|
83
|
+
if (FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY)) {
|
84
|
+
fiobj_ary_push(data->stack, (FIOBJ)data->count);
|
85
|
+
fiobj_ary_push(data->stack, (FIOBJ)data->rb);
|
86
|
+
data->count = fiobj_ary_count(o);
|
87
|
+
data->rb = rb_tmp;
|
88
|
+
} else if (FIOBJ_TYPE_IS(o, FIOBJ_T_HASH)) {
|
89
|
+
fiobj_ary_push(data->stack, (FIOBJ)data->count);
|
90
|
+
fiobj_ary_push(data->stack, (FIOBJ)data->rb);
|
91
|
+
data->count = fiobj_hash_count(o);
|
92
|
+
data->rb = rb_tmp;
|
93
|
+
}
|
94
|
+
while (data->count == 0 && fiobj_ary_count(data->stack)) {
|
95
|
+
data->rb = fiobj_ary_pop(data->stack);
|
96
|
+
data->count = fiobj_ary_pop(data->stack);
|
97
|
+
}
|
98
|
+
return 0;
|
99
|
+
}
|
100
|
+
|
101
|
+
static inline VALUE fiobj2rb_deep(FIOBJ obj, uint8_t str2sym) {
|
102
|
+
fiobj2rb_s data = {.stack = fiobj_ary_new2(4), .str2sym = str2sym};
|
103
|
+
|
104
|
+
/* deep copy */
|
105
|
+
fiobj_each2(obj, fiobj2rb_task, &data);
|
106
|
+
/* cleanup (shouldn't happen, but what the hell)... */
|
107
|
+
while (fiobj_ary_pop(data.stack))
|
108
|
+
;
|
109
|
+
fiobj_free(data.stack);
|
110
|
+
Registry.remove(data.rb);
|
111
|
+
return data.rb;
|
112
|
+
}
|
113
|
+
|
114
|
+
// require 'iodine'
|
115
|
+
// Iodine::JSON.parse "{\"1\":[1,2,3,4]}"
|
116
|
+
// Iodine::JSON.parse IO.binread("")
|
117
|
+
#endif /* H_RB_FIOBJ2RUBY_H */
|