rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -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 +23 -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/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,79 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_REDIS_ENGINE_H
8
+ #define H_REDIS_ENGINE_H
9
+
10
+ #include <fio.h>
11
+ #include <fiobj.h>
12
+
13
+ /* support C++ */
14
+ #ifdef __cplusplus
15
+ extern "C" {
16
+ #endif
17
+
18
+ /** possible arguments for the `redis_engine_create` function call */
19
+ struct redis_engine_create_args {
20
+ /** Redis server's address, defaults to localhost. */
21
+ fio_str_info_s address;
22
+ /** Redis server's port, defaults to 6379. */
23
+ fio_str_info_s port;
24
+ /** Redis server's password, if any. */
25
+ fio_str_info_s auth;
26
+ /** A `ping` will be sent every `ping_interval` interval or inactivity. */
27
+ uint8_t ping_interval;
28
+ };
29
+
30
+ /**
31
+ * See the {fio.h} file for documentation about engines.
32
+ *
33
+ * The engine is active only after facil.io starts running.
34
+ *
35
+ * A `ping` will be sent every `ping_interval` interval or inactivity. The
36
+ * default value (0) will fallback to facil.io's maximum time of inactivity (5
37
+ * minutes) before polling on the connection's protocol.
38
+ *
39
+ * function names speak for themselves ;-)
40
+ *
41
+ * Note: The Redis engine assumes it will stay alive until all the messages and
42
+ * callbacks have been called (or facil.io exits)... If the engine is destroyed
43
+ * midway, memory leaks might occur (and little puppies might cry).
44
+ */
45
+ fio_pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
46
+ #define redis_engine_create(...) \
47
+ redis_engine_create((struct redis_engine_create_args){__VA_ARGS__})
48
+
49
+ /**
50
+ * Sends a Redis command through the engine's connection.
51
+ *
52
+ * The response will be sent back using the optional callback. `udata` is passed
53
+ * along untouched.
54
+ *
55
+ * The message will be resent on network failures, until a response validates
56
+ * the fact that the command was sent (or the engine is destroyed).
57
+ *
58
+ * Note: NEVER call Pub/Sub commands using this function, as it will violate the
59
+ * Redis connection's protocol (best case scenario, a disconnection will occur
60
+ * before and messages are lost).
61
+ */
62
+ intptr_t redis_engine_send(fio_pubsub_engine_s *engine, FIOBJ command,
63
+ void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply,
64
+ void *udata),
65
+ void *udata);
66
+
67
+ /**
68
+ * See the {pubsub.h} file for documentation about engines.
69
+ *
70
+ * function names speak for themselves ;-)
71
+ */
72
+ void redis_engine_destroy(fio_pubsub_engine_s *engine);
73
+
74
+ /* support C++ */
75
+ #ifdef __cplusplus
76
+ }
77
+ #endif
78
+
79
+ #endif /* H_REDIS_ENGINE_H */
@@ -0,0 +1,317 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_RESP_PARSER_H
8
+ /**
9
+ * This single file library is a RESP parser for Redis connections.
10
+ *
11
+ * To use this file, the `.c` file in which this file is included MUST define a
12
+ * number of callbacks, as later inticated.
13
+ *
14
+ * When feeding the parser, the parser will inform of any trailing bytes (bytes
15
+ * at the end of the buffer that could not be parsed). These bytes should be
16
+ * resent to the parser along with more data. Zero is a valid return value.
17
+ *
18
+ * Note: mostly, callback return vaslues are ignored.
19
+ */
20
+ #define H_RESP_PARSER_H
21
+
22
+ #ifndef _GNU_SOURCE
23
+ #define _GNU_SOURCE
24
+ #endif
25
+
26
+ #include <stdint.h>
27
+ #include <stdlib.h>
28
+ #include <string.h>
29
+
30
+ /* *****************************************************************************
31
+ The Parser
32
+ ***************************************************************************** */
33
+
34
+ typedef struct resp_parser_s {
35
+ /* for internal use - (array / object countdown) */
36
+ intptr_t obj_countdown;
37
+ /* for internal use - (string byte consumption) */
38
+ intptr_t expecting;
39
+ } resp_parser_s;
40
+
41
+ /**
42
+ * Returns the number of bytes to be resent. i.e., for a return value 5, the
43
+ * last 5 bytes in the buffer need to be resent to the parser.
44
+ */
45
+ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
46
+ size_t length);
47
+
48
+ /* *****************************************************************************
49
+ Required Parser Callbacks (to be defined by the including file)
50
+ ***************************************************************************** */
51
+
52
+ /** a local static callback, called when the RESP message is complete. */
53
+ static int resp_on_message(resp_parser_s *parser);
54
+
55
+ /** a local static callback, called when a Number object is parsed. */
56
+ static int resp_on_number(resp_parser_s *parser, int64_t num);
57
+ /** a local static callback, called when a OK message is received. */
58
+ static int resp_on_okay(resp_parser_s *parser);
59
+ /** a local static callback, called when NULL is received. */
60
+ static int resp_on_null(resp_parser_s *parser);
61
+
62
+ /**
63
+ * a local static callback, called when a String should be allocated.
64
+ *
65
+ * `str_len` is the expected number of bytes that will fill the final string
66
+ * object, without any NUL byte marker (the string might be binary).
67
+ *
68
+ * If this function returns any value besides 0, parsing is stopped.
69
+ */
70
+ static int resp_on_start_string(resp_parser_s *parser, size_t str_len);
71
+ /** a local static callback, called as String objects are streamed. */
72
+ static int resp_on_string_chunk(resp_parser_s *parser, void *data, size_t len);
73
+ /** a local static callback, called when a String object had finished streaming.
74
+ */
75
+ static int resp_on_end_string(resp_parser_s *parser);
76
+
77
+ /** a local static callback, called an error message is received. */
78
+ static int resp_on_err_msg(resp_parser_s *parser, void *data, size_t len);
79
+
80
+ /**
81
+ * a local static callback, called when an Array should be allocated.
82
+ *
83
+ * `array_len` is the expected number of objects that will fill the Array
84
+ * object.
85
+ *
86
+ * There's no `resp_on_end_array` callback since the RESP protocol assumes the
87
+ * message is finished along with the Array (`resp_on_message` is called).
88
+ * However, just in case a non-conforming client/server sends nested Arrays, the
89
+ * callback should test against possible overflow or nested Array endings.
90
+ *
91
+ * If this function returns any value besides 0, parsing is stopped.
92
+ */
93
+ static int resp_on_start_array(resp_parser_s *parser, size_t array_len);
94
+
95
+ /** a local static callback, called when a parser / protocol error occurs. */
96
+ static int resp_on_parser_error(resp_parser_s *parser);
97
+
98
+ /* *****************************************************************************
99
+ Seeking the new line...
100
+ ***************************************************************************** */
101
+
102
+ #if FIO_MEMCHAR
103
+
104
+ /**
105
+ * This seems to be faster on some systems, especially for smaller distances.
106
+ *
107
+ * On newer systems, `memchr` should be faster.
108
+ */
109
+ static inline int seek2ch(uint8_t **buffer, register const uint8_t *limit,
110
+ const uint8_t c) {
111
+ if (**buffer == c)
112
+ return 1;
113
+
114
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
115
+ /* too short for this mess */
116
+ if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
117
+ goto finish;
118
+
119
+ /* align memory */
120
+ {
121
+ const uint8_t *alignment =
122
+ (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
123
+ if (limit >= alignment) {
124
+ while (*buffer < alignment) {
125
+ if (**buffer == c)
126
+ return 1;
127
+ *buffer += 1;
128
+ }
129
+ }
130
+ }
131
+ const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
132
+ #else
133
+ const uint8_t *limit64 = (uint8_t *)limit - 7;
134
+ #endif
135
+ uint64_t wanted1 = 0x0101010101010101ULL * c;
136
+ for (; *buffer < limit64; *buffer += 8) {
137
+ const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
138
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
139
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
140
+ if ((t0 & t1)) {
141
+ break;
142
+ }
143
+ }
144
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
145
+ finish:
146
+ #endif
147
+ while (*buffer < limit) {
148
+ if (**buffer == c)
149
+ return 1;
150
+ (*buffer)++;
151
+ }
152
+ return 0;
153
+ }
154
+
155
+ #else
156
+
157
+ /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
158
+ inline static uint8_t seek2ch(uint8_t **pos, const uint8_t *limit, uint8_t ch) {
159
+ /* This is library based alternative that is sometimes slower */
160
+ if (*pos >= limit || **pos == ch) {
161
+ return 0;
162
+ }
163
+ uint8_t *tmp = (uint8_t *)memchr(*pos, ch, limit - (*pos));
164
+ if (tmp) {
165
+ *pos = tmp;
166
+ return 1;
167
+ }
168
+ *pos = (uint8_t *)limit;
169
+ return 0;
170
+ }
171
+
172
+ #endif
173
+
174
+ /* *****************************************************************************
175
+ Parsing RESP requests
176
+ ***************************************************************************** */
177
+
178
+ /**
179
+ * Returns the number of bytes to be resent. i.e., for a return value 5, the
180
+ * last 5 bytes in the buffer need to be resent to the parser.
181
+ */
182
+ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
183
+ size_t length) {
184
+ if (!parser->obj_countdown)
185
+ parser->obj_countdown = 1; /* always expect something... */
186
+ uint8_t *pos = (uint8_t *)buffer;
187
+ const uint8_t *stop = pos + length;
188
+ while (pos < stop) {
189
+ uint8_t *eol;
190
+ if (parser->expecting) {
191
+ if (pos + parser->expecting + 2 > stop) {
192
+ /* read, but make sure the buffer includes the new line markers */
193
+ size_t tmp = (size_t)((uintptr_t)stop - (uintptr_t)pos);
194
+ if ((intptr_t)tmp >= parser->expecting)
195
+ tmp = parser->expecting - 1;
196
+ resp_on_string_chunk(parser, (void *)pos, tmp);
197
+ parser->expecting -= tmp;
198
+ return (size_t)((uintptr_t)stop - ((uintptr_t)pos + tmp)); /* 0 or 1 */
199
+ } else {
200
+ resp_on_string_chunk(parser, (void *)pos, parser->expecting);
201
+ resp_on_end_string(parser);
202
+ pos += parser->expecting;
203
+ if (pos[0] == '\r')
204
+ ++pos;
205
+ if (pos[0] == '\n')
206
+ ++pos;
207
+ parser->expecting = 0;
208
+ --parser->obj_countdown;
209
+ if (parser->obj_countdown <= 0) {
210
+ parser->obj_countdown = 1;
211
+ if (resp_on_message(parser))
212
+ goto finish;
213
+ }
214
+ continue;
215
+ }
216
+ }
217
+ eol = pos;
218
+ if (seek2ch(&eol, stop, '\n') == 0)
219
+ break;
220
+ switch (*pos) {
221
+ case '+':
222
+ if (pos[1] == 'O' && pos[2] == 'K' && pos[3] == '\r' && pos[4] == '\n') {
223
+ resp_on_okay(parser);
224
+ --parser->obj_countdown;
225
+ break;
226
+ }
227
+ if (resp_on_start_string(parser,
228
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 2))) {
229
+ pos = eol + 1;
230
+ goto finish;
231
+ }
232
+ resp_on_string_chunk(parser, (void *)(pos + 1),
233
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 1));
234
+ resp_on_end_string(parser);
235
+ --parser->obj_countdown;
236
+ break;
237
+ case '-':
238
+ resp_on_err_msg(parser, pos,
239
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 1));
240
+ --parser->obj_countdown;
241
+ break;
242
+ case '*': /* fallthrough */
243
+ case '$': /* fallthrough */
244
+ case ':': {
245
+ uint8_t id = *pos;
246
+ uint8_t inv = 0;
247
+ int64_t i = 0;
248
+ ++pos;
249
+ if (pos[0] == '-') {
250
+ inv = 1;
251
+ ++pos;
252
+ }
253
+ while ((size_t)(pos[0] - (uint8_t)'0') <= 9) {
254
+ i = (i * 10) + (pos[0] - ((uint8_t)'0'));
255
+ ++pos;
256
+ }
257
+ if (inv)
258
+ i = i * -1;
259
+
260
+ switch (id) {
261
+ case ':':
262
+ resp_on_number(parser, i);
263
+ --parser->obj_countdown;
264
+ break;
265
+ case '$':
266
+ if (i < 0) {
267
+ resp_on_null(parser);
268
+ --parser->obj_countdown;
269
+ } else if (i == 0) {
270
+ resp_on_start_string(parser, 0);
271
+ resp_on_end_string(parser);
272
+ --parser->obj_countdown;
273
+ eol += 2; /* consume the extra "\r\n" */
274
+ } else {
275
+ if (resp_on_start_string(parser, i)) {
276
+ pos = eol + 1;
277
+ goto finish;
278
+ }
279
+ parser->expecting = i;
280
+ }
281
+ break;
282
+ case '*':
283
+ if (i < 0) {
284
+ resp_on_null(parser);
285
+ } else {
286
+ if (resp_on_start_array(parser, i)) {
287
+ pos = eol + 1;
288
+ goto finish;
289
+ }
290
+ parser->obj_countdown += i;
291
+ }
292
+ --parser->obj_countdown;
293
+ break;
294
+ }
295
+ } break;
296
+ default:
297
+ if (!parser->obj_countdown && !parser->expecting) {
298
+ /* possible (probable) inline command... for server authoring. */
299
+ /* Not Supported, PRs are welcome. */
300
+ resp_on_parser_error(parser);
301
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
302
+ } else {
303
+ resp_on_parser_error(parser);
304
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
305
+ }
306
+ }
307
+ pos = eol + 1;
308
+ if (parser->obj_countdown <= 0 && !parser->expecting) {
309
+ parser->obj_countdown = 1;
310
+ resp_on_message(parser);
311
+ }
312
+ }
313
+ finish:
314
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
315
+ }
316
+
317
+ #endif /* H_RESP_PARSER_H */
@@ -0,0 +1,173 @@
1
+ #include "iodine.h"
2
+ #include "iodine_store.h"
3
+ #include "ruby.h"
4
+
5
+ #include <stddef.h>
6
+ #include <stdint.h>
7
+ #include <stdio.h>
8
+ // clang-format on
9
+
10
+ #include "fio.h"
11
+
12
+ #define IO_MAX_READ 8192
13
+
14
+ static ID call_id;
15
+ static uint8_t ATTACH_ON_READ_READY_CALLBACK;
16
+ static uint8_t ATTACH_ON_WRITE_READY_CALLBACK;
17
+
18
+ /* *****************************************************************************
19
+ Fiber Scheduler API
20
+ ***************************************************************************** */
21
+
22
+ static void noop(intptr_t uuid, fio_protocol_s *protocol) {
23
+ (void)uuid;
24
+ (void)protocol;
25
+ }
26
+
27
+ typedef struct {
28
+ fio_protocol_s p;
29
+ VALUE block;
30
+ } scheduler_protocol_s;
31
+
32
+ static void iodine_scheduler_perform(intptr_t uuid, fio_protocol_s *fio_protocol) {
33
+ scheduler_protocol_s *protocol = (scheduler_protocol_s *)fio_protocol;
34
+ VALUE block = protocol->block;
35
+
36
+ IodineCaller.call(block, call_id);
37
+
38
+ IodineStore.remove(block);
39
+ fio_free(protocol);
40
+
41
+ (void)uuid;
42
+ }
43
+
44
+ static VALUE iodine_scheduler_attach(VALUE self, VALUE r_fd, VALUE r_waittype, VALUE r_timeout) {
45
+ Check_Type(r_fd, T_FIXNUM);
46
+ int fd = FIX2INT(r_fd);
47
+
48
+ Check_Type(r_waittype, T_FIXNUM);
49
+ size_t waittype = FIX2UINT(r_waittype);
50
+
51
+ Check_Type(r_timeout, T_FIXNUM);
52
+ size_t timeout = FIX2UINT(r_timeout);
53
+
54
+ fio_set_non_block(fd);
55
+
56
+ rb_need_block();
57
+ VALUE block = IodineStore.add(rb_block_proc());
58
+
59
+ scheduler_protocol_s *protocol = fio_malloc(sizeof(*protocol));
60
+ FIO_ASSERT_ALLOC(protocol);
61
+
62
+ if ((waittype & ATTACH_ON_READ_READY_CALLBACK) && (waittype & ATTACH_ON_WRITE_READY_CALLBACK)) {
63
+ *protocol = (scheduler_protocol_s){
64
+ .p.on_data = iodine_scheduler_perform,
65
+ .p.on_ready = iodine_scheduler_perform,
66
+ .p.on_close = noop,
67
+ .p.ping = noop,
68
+ .block = block,
69
+ };
70
+ } else if (waittype & ATTACH_ON_READ_READY_CALLBACK) {
71
+ *protocol = (scheduler_protocol_s){
72
+ .p.on_data = iodine_scheduler_perform,
73
+ .p.on_ready = noop,
74
+ .p.on_close = noop,
75
+ .p.ping = noop,
76
+ .block = block,
77
+ };
78
+ } else if (waittype & ATTACH_ON_WRITE_READY_CALLBACK) {
79
+ *protocol = (scheduler_protocol_s){
80
+ .p.on_data = noop,
81
+ .p.on_ready = iodine_scheduler_perform,
82
+ .p.on_close = noop,
83
+ .p.ping = noop,
84
+ .block = block,
85
+ };
86
+ }
87
+
88
+ intptr_t uuid = fio_fd2uuid(fd);
89
+ if (timeout) {
90
+ fio_timeout_set(uuid, timeout);
91
+ }
92
+
93
+ fio_watch(uuid, (fio_protocol_s *)protocol);
94
+
95
+ return LONG2NUM(uuid);
96
+ (void)self;
97
+ }
98
+
99
+ static VALUE iodine_scheduler_write(VALUE self, VALUE r_fd, VALUE r_buffer, VALUE r_length, VALUE r_offset) {
100
+ Check_Type(r_fd, T_FIXNUM);
101
+ int fd = FIX2INT(r_fd);
102
+
103
+ Check_Type(r_buffer, T_STRING);
104
+ char *buffer = RSTRING_PTR(r_buffer);
105
+
106
+ Check_Type(r_length, T_FIXNUM);
107
+ int length = FIX2INT(r_length);
108
+
109
+ Check_Type(r_offset, T_FIXNUM);
110
+ int offset = FIX2INT(r_offset);
111
+
112
+ fio_write2(fio_fd2uuid(fd), .data.buffer = buffer, .length = length, .offset = offset,
113
+ .after.dealloc = FIO_DEALLOC_NOOP);
114
+
115
+ return r_length;
116
+
117
+ (void)self;
118
+ }
119
+
120
+ static VALUE iodine_scheduler_read(VALUE self, VALUE r_fd, VALUE r_length, VALUE r_offset) {
121
+ Check_Type(r_fd, T_FIXNUM);
122
+ int fd = FIX2INT(r_fd);
123
+
124
+ Check_Type(r_length, T_FIXNUM);
125
+ int length = FIX2INT(r_length);
126
+
127
+ if (length == 0) {
128
+ length = IO_MAX_READ;
129
+ }
130
+
131
+ intptr_t uuid = fio_fd2uuid(fd);
132
+ char buffer[length];
133
+
134
+ ssize_t len = fio_read_unsafe(uuid, &buffer, length);
135
+ if (len == -1) {
136
+ return Qnil;
137
+ }
138
+
139
+ return rb_str_new(buffer, len);
140
+
141
+ (void)self;
142
+ (void)r_offset;
143
+ }
144
+
145
+ static VALUE iodine_scheduler_close(VALUE self) {
146
+ fio_defer_perform();
147
+ while (fio_flush_all()) {}
148
+
149
+ return Qtrue;
150
+ (void)self;
151
+ }
152
+
153
+ /* *****************************************************************************
154
+ Scheduler initialization
155
+ ***************************************************************************** */
156
+
157
+ void iodine_scheduler_initialize(void) {
158
+ call_id = rb_intern2("call", 4);
159
+
160
+ VALUE SchedulerModule = rb_define_module_under(IodineModule, "Scheduler");
161
+
162
+ rb_define_module_function(SchedulerModule, "attach", iodine_scheduler_attach, 3);
163
+ rb_define_module_function(SchedulerModule, "write", iodine_scheduler_write, 4);
164
+ rb_define_module_function(SchedulerModule, "read", iodine_scheduler_read, 3);
165
+ rb_define_module_function(SchedulerModule, "close", iodine_scheduler_close, 0);
166
+
167
+ VALUE cIO = rb_const_get(rb_cObject, rb_intern2("IO", 2));
168
+ VALUE io_readable = rb_const_get(cIO, rb_intern2("READABLE", 8));
169
+ VALUE io_writable = rb_const_get(cIO, rb_intern2("WRITABLE", 8));
170
+
171
+ ATTACH_ON_READ_READY_CALLBACK = NUM2SHORT(io_readable);
172
+ ATTACH_ON_WRITE_READY_CALLBACK = NUM2SHORT(io_writable);
173
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef H_SCHEDULER_H
2
+ #define H_SCHEDULER_H
3
+
4
+ void iodine_scheduler_initialize(void);
5
+
6
+ #endif