io-event 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,294 @@
1
+ // Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in
11
+ // all copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ // THE SOFTWARE.
20
+
21
+ #include "selector.h"
22
+ #include <fcntl.h>
23
+
24
+ static const int DEBUG = 0;
25
+
26
+ static ID id_transfer, id_alive_p;
27
+
28
+ VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv) {
29
+ // TODO Consider introducing something like `rb_fiber_scheduler_transfer(...)`.
30
+ #ifdef HAVE__RB_FIBER_TRANSFER
31
+ if (RTEST(rb_obj_is_fiber(fiber))) {
32
+ if (RTEST(rb_fiber_alive_p(fiber))) {
33
+ return rb_fiber_transfer(fiber, argc, argv);
34
+ }
35
+
36
+ return Qnil;
37
+ }
38
+ #endif
39
+ if (RTEST(rb_funcall(fiber, id_alive_p, 0))) {
40
+ return rb_funcallv(fiber, id_transfer, argc, argv);
41
+ }
42
+
43
+ return Qnil;
44
+ }
45
+
46
+ #ifndef HAVE__RB_FIBER_RAISE
47
+ static ID id_raise;
48
+
49
+ VALUE IO_Event_Selector_fiber_raise(VALUE fiber, int argc, VALUE *argv) {
50
+ return rb_funcallv(fiber, id_raise, argc, argv);
51
+ }
52
+ #endif
53
+
54
+ #ifndef HAVE_RB_FIBER_CURRENT
55
+ static ID id_current;
56
+
57
+ static VALUE rb_fiber_current() {
58
+ return rb_funcall(rb_cFiber, id_current, 0);
59
+ }
60
+ #endif
61
+
62
+
63
+ #ifndef HAVE_RB_IO_DESCRIPTOR
64
+ static ID id_fileno;
65
+
66
+ int IO_Event_Selector_io_descriptor(VALUE io) {
67
+ return RB_NUM2INT(rb_funcall(io, id_fileno, 0));
68
+ }
69
+ #endif
70
+
71
+ #ifndef HAVE_RB_PROCESS_STATUS_WAIT
72
+ static ID id_wait;
73
+ static VALUE rb_Process_Status = Qnil;
74
+
75
+ VALUE IO_Event_Selector_process_status_wait(rb_pid_t pid)
76
+ {
77
+ return rb_funcall(rb_Process_Status, id_wait, 2, PIDT2NUM(pid), INT2NUM(WNOHANG));
78
+ }
79
+ #endif
80
+
81
+ int IO_Event_Selector_nonblock_set(int file_descriptor)
82
+ {
83
+ int flags = fcntl(file_descriptor, F_GETFL, 0);
84
+
85
+ if (!(flags & O_NONBLOCK)) {
86
+ fcntl(file_descriptor, F_SETFL, flags | O_NONBLOCK);
87
+ }
88
+
89
+ return flags;
90
+ }
91
+
92
+ void IO_Event_Selector_nonblock_restore(int file_descriptor, int flags)
93
+ {
94
+ if (!(flags & O_NONBLOCK)) {
95
+ fcntl(file_descriptor, F_SETFL, flags & ~flags);
96
+ }
97
+ }
98
+
99
+ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
100
+ id_transfer = rb_intern("transfer");
101
+ id_alive_p = rb_intern("alive?");
102
+
103
+ #ifndef HAVE__RB_FIBER_RAISE
104
+ id_raise = rb_intern("raise");
105
+ #endif
106
+
107
+ #ifndef HAVE_RB_FIBER_CURRENT
108
+ id_current = rb_intern("current");
109
+ #endif
110
+
111
+ #ifndef HAVE_RB_IO_DESCRIPTOR
112
+ id_fileno = rb_intern("fileno");
113
+ #endif
114
+
115
+ #ifndef HAVE_RB_PROCESS_STATUS_WAIT
116
+ id_wait = rb_intern("wait");
117
+ rb_Process_Status = rb_const_get_at(rb_mProcess, rb_intern("Status"));
118
+ rb_gc_register_mark_object(rb_Process_Status);
119
+ #endif
120
+ }
121
+
122
+ struct wait_and_transfer_arguments {
123
+ int argc;
124
+ VALUE *argv;
125
+
126
+ struct IO_Event_Selector *backend;
127
+ struct IO_Event_Selector_Queue *waiting;
128
+ };
129
+
130
+ static void queue_pop(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *waiting) {
131
+ if (waiting->behind) {
132
+ waiting->behind->infront = waiting->infront;
133
+ } else {
134
+ backend->waiting = waiting->infront;
135
+ }
136
+
137
+ if (waiting->infront) {
138
+ waiting->infront->behind = waiting->behind;
139
+ } else {
140
+ backend->ready = waiting->behind;
141
+ }
142
+ }
143
+
144
+ static void queue_push(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *waiting) {
145
+ if (backend->waiting) {
146
+ backend->waiting->behind = waiting;
147
+ waiting->infront = backend->waiting;
148
+ } else {
149
+ backend->ready = waiting;
150
+ }
151
+
152
+ backend->waiting = waiting;
153
+ }
154
+
155
+ static VALUE wait_and_transfer(VALUE _arguments) {
156
+ struct wait_and_transfer_arguments *arguments = (struct wait_and_transfer_arguments *)_arguments;
157
+
158
+ VALUE fiber = arguments->argv[0];
159
+ int argc = arguments->argc - 1;
160
+ VALUE *argv = arguments->argv + 1;
161
+
162
+ return IO_Event_Selector_fiber_transfer(fiber, argc, argv);
163
+ }
164
+
165
+ static VALUE wait_and_transfer_ensure(VALUE _arguments) {
166
+ struct wait_and_transfer_arguments *arguments = (struct wait_and_transfer_arguments *)_arguments;
167
+
168
+ queue_pop(arguments->backend, arguments->waiting);
169
+
170
+ return Qnil;
171
+ }
172
+
173
+ VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALUE *argv)
174
+ {
175
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
176
+
177
+ struct IO_Event_Selector_Queue waiting = {
178
+ .behind = NULL,
179
+ .infront = NULL,
180
+ .flags = IO_EVENT_SELECTOR_QUEUE_FIBER,
181
+ .fiber = rb_fiber_current()
182
+ };
183
+
184
+ queue_push(backend, &waiting);
185
+
186
+ struct wait_and_transfer_arguments arguments = {
187
+ .argc = argc,
188
+ .argv = argv,
189
+ .backend = backend,
190
+ .waiting = &waiting,
191
+ };
192
+
193
+ return rb_ensure(wait_and_transfer, (VALUE)&arguments, wait_and_transfer_ensure, (VALUE)&arguments);
194
+ }
195
+
196
+ static VALUE wait_and_raise(VALUE _arguments) {
197
+ struct wait_and_transfer_arguments *arguments = (struct wait_and_transfer_arguments *)_arguments;
198
+
199
+ VALUE fiber = arguments->argv[0];
200
+ int argc = arguments->argc - 1;
201
+ VALUE *argv = arguments->argv + 1;
202
+
203
+ return IO_Event_Selector_fiber_raise(fiber, argc, argv);
204
+ }
205
+
206
+ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv)
207
+ {
208
+ rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
209
+
210
+ struct IO_Event_Selector_Queue waiting = {
211
+ .behind = NULL,
212
+ .infront = NULL,
213
+ .flags = IO_EVENT_SELECTOR_QUEUE_FIBER,
214
+ .fiber = rb_fiber_current()
215
+ };
216
+
217
+ queue_push(backend, &waiting);
218
+
219
+ struct wait_and_transfer_arguments arguments = {
220
+ .argc = argc,
221
+ .argv = argv,
222
+ .backend = backend,
223
+ .waiting = &waiting,
224
+ };
225
+
226
+ return rb_ensure(wait_and_raise, (VALUE)&arguments, wait_and_transfer_ensure, (VALUE)&arguments);
227
+ }
228
+
229
+ void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber)
230
+ {
231
+ struct IO_Event_Selector_Queue *waiting = malloc(sizeof(struct IO_Event_Selector_Queue));
232
+
233
+ waiting->behind = NULL;
234
+ waiting->infront = NULL;
235
+ waiting->flags = IO_EVENT_SELECTOR_QUEUE_INTERNAL;
236
+ waiting->fiber = fiber;
237
+
238
+ queue_push(backend, waiting);
239
+ }
240
+
241
+ static inline
242
+ void IO_Event_Selector_queue_pop(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *ready)
243
+ {
244
+ if (DEBUG) fprintf(stderr, "IO_Event_Selector_queue_pop -> %p\n", (void*)ready->fiber);
245
+ if (ready->flags & IO_EVENT_SELECTOR_QUEUE_FIBER) {
246
+ IO_Event_Selector_fiber_transfer(ready->fiber, 0, NULL);
247
+ } else {
248
+ VALUE fiber = ready->fiber;
249
+ queue_pop(backend, ready);
250
+ free(ready);
251
+
252
+ if (RTEST(rb_funcall(fiber, id_alive_p, 0))) {
253
+ rb_funcall(fiber, id_transfer, 0);
254
+ }
255
+ }
256
+ }
257
+
258
+ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
259
+ {
260
+ int count = 0;
261
+
262
+ // Get the current tail and head of the queue:
263
+ struct IO_Event_Selector_Queue *waiting = backend->waiting;
264
+ if (DEBUG) fprintf(stderr, "IO_Event_Selector_queue_flush waiting = %p\n", waiting);
265
+
266
+ // Process from head to tail in order:
267
+ // During this, more items may be appended to tail.
268
+ while (backend->ready) {
269
+ if (DEBUG) fprintf(stderr, "backend->ready = %p\n", backend->ready);
270
+ struct IO_Event_Selector_Queue *ready = backend->ready;
271
+
272
+ count += 1;
273
+ IO_Event_Selector_queue_pop(backend, ready);
274
+
275
+ if (ready == waiting) break;
276
+ }
277
+
278
+ return count;
279
+ }
280
+
281
+ void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration)
282
+ {
283
+ if ((stop->tv_nsec - start->tv_nsec) < 0) {
284
+ duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
285
+ duration->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
286
+ } else {
287
+ duration->tv_sec = stop->tv_sec - start->tv_sec;
288
+ duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
289
+ }
290
+ }
291
+
292
+ void IO_Event_Selector_current_time(struct timespec *time) {
293
+ clock_gettime(CLOCK_MONOTONIC, time);
294
+ }
@@ -0,0 +1,123 @@
1
+ // Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in
11
+ // all copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ // THE SOFTWARE.
20
+
21
+ #include <ruby.h>
22
+ #include <ruby/thread.h>
23
+ #include <ruby/io.h>
24
+
25
+ #ifdef HAVE_RUBY_IO_BUFFER_H
26
+ #include <ruby/io/buffer.h>
27
+ #endif
28
+
29
+ #include <time.h>
30
+
31
+ enum IO_Event {
32
+ IO_EVENT_READABLE = 1,
33
+ IO_EVENT_PRIORITY = 2,
34
+ IO_EVENT_WRITABLE = 4,
35
+ IO_EVENT_ERROR = 8,
36
+ IO_EVENT_HANGUP = 16
37
+ };
38
+
39
+ void Init_IO_Event_Selector();
40
+
41
+ VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv);
42
+
43
+ #ifdef HAVE__RB_FIBER_RAISE
44
+ #define IO_Event_Selector_fiber_raise(fiber, argc, argv) rb_fiber_raise(fiber, argc, argv)
45
+ #else
46
+ VALUE IO_Event_Selector_fiber_raise(VALUE fiber, int argc, VALUE *argv);
47
+ #endif
48
+
49
+ #ifdef HAVE_RB_IO_DESCRIPTOR
50
+ #define IO_Event_Selector_io_descriptor(io) rb_io_descriptor(io)
51
+ #else
52
+ int IO_Event_Selector_io_descriptor(VALUE io);
53
+ #endif
54
+
55
+ #ifdef HAVE_RB_PROCESS_STATUS_WAIT
56
+ #define IO_Event_Selector_process_status_wait(pid) rb_process_status_wait(pid)
57
+ #else
58
+ VALUE IO_Event_Selector_process_status_wait(rb_pid_t pid);
59
+ #endif
60
+
61
+ int IO_Event_Selector_nonblock_set(int file_descriptor);
62
+ void IO_Event_Selector_nonblock_restore(int file_descriptor, int flags);
63
+
64
+ enum IO_Event_Selector_Queue_Flags {
65
+ IO_EVENT_SELECTOR_QUEUE_FIBER = 1,
66
+ IO_EVENT_SELECTOR_QUEUE_INTERNAL = 2,
67
+ };
68
+
69
+ struct IO_Event_Selector_Queue {
70
+ struct IO_Event_Selector_Queue *behind;
71
+ struct IO_Event_Selector_Queue *infront;
72
+
73
+ enum IO_Event_Selector_Queue_Flags flags;
74
+
75
+ VALUE fiber;
76
+ };
77
+
78
+ struct IO_Event_Selector {
79
+ VALUE loop;
80
+
81
+ struct IO_Event_Selector_Queue *free;
82
+
83
+ // Append to waiting.
84
+ struct IO_Event_Selector_Queue *waiting;
85
+ // Process from ready.
86
+ struct IO_Event_Selector_Queue *ready;
87
+ };
88
+
89
+ static inline
90
+ void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE loop) {
91
+ backend->loop = loop;
92
+ backend->waiting = NULL;
93
+ backend->ready = NULL;
94
+ }
95
+
96
+ static inline
97
+ void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
98
+ rb_gc_mark(backend->loop);
99
+
100
+ struct IO_Event_Selector_Queue *ready = backend->ready;
101
+ while (ready) {
102
+ rb_gc_mark(ready->fiber);
103
+ ready = ready->behind;
104
+ }
105
+ }
106
+
107
+ VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALUE *argv);
108
+ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv);
109
+
110
+ static inline
111
+ VALUE IO_Event_Selector_yield(struct IO_Event_Selector *backend)
112
+ {
113
+ return IO_Event_Selector_resume(backend, 1, &backend->loop);
114
+ }
115
+
116
+ void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber);
117
+ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend);
118
+
119
+ void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration);
120
+ void IO_Event_Selector_current_time(struct timespec *time);
121
+
122
+ #define PRINTF_TIMESPEC "%lld.%.9ld"
123
+ #define PRINTF_TIMESPEC_ARGS(ts) (long long)((ts).tv_sec), (ts).tv_nsec