io-event-machty 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
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
+ #pragma once
22
+
23
+ #include <ruby.h>
24
+
25
+ #define IO_EVENT_SELECTOR_KQUEUE
26
+
27
+ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector);
@@ -0,0 +1,36 @@
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 <sys/types.h>
22
+ #include <sys/syscall.h>
23
+ #include <unistd.h>
24
+ #include <poll.h>
25
+ #include <stdlib.h>
26
+ #include <stdio.h>
27
+
28
+ #ifndef __NR_pidfd_open
29
+ #define __NR_pidfd_open 434 /* System call # on most architectures */
30
+ #endif
31
+
32
+ static int
33
+ pidfd_open(pid_t pid, unsigned int flags)
34
+ {
35
+ return syscall(__NR_pidfd_open, pid, flags);
36
+ }
@@ -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,130 @@
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
+ #pragma once
22
+
23
+ #include <ruby.h>
24
+ #include <ruby/thread.h>
25
+ #include <ruby/io.h>
26
+
27
+ #ifdef HAVE_RUBY_IO_BUFFER_H
28
+ #include <ruby/io/buffer.h>
29
+ #include <ruby/fiber/scheduler.h>
30
+ #endif
31
+
32
+ #include <time.h>
33
+
34
+ enum IO_Event {
35
+ IO_EVENT_READABLE = 1,
36
+ IO_EVENT_PRIORITY = 2,
37
+ IO_EVENT_WRITABLE = 4,
38
+ IO_EVENT_ERROR = 8,
39
+ IO_EVENT_HANGUP = 16
40
+ };
41
+
42
+ void Init_IO_Event_Selector();
43
+
44
+ static inline int IO_Event_try_again(int error) {
45
+ return error == EAGAIN || error == EWOULDBLOCK;
46
+ }
47
+
48
+ VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv);
49
+
50
+ #ifdef HAVE__RB_FIBER_RAISE
51
+ #define IO_Event_Selector_fiber_raise(fiber, argc, argv) rb_fiber_raise(fiber, argc, argv)
52
+ #else
53
+ VALUE IO_Event_Selector_fiber_raise(VALUE fiber, int argc, VALUE *argv);
54
+ #endif
55
+
56
+ #ifdef HAVE_RB_IO_DESCRIPTOR
57
+ #define IO_Event_Selector_io_descriptor(io) rb_io_descriptor(io)
58
+ #else
59
+ int IO_Event_Selector_io_descriptor(VALUE io);
60
+ #endif
61
+
62
+ #ifdef HAVE_RB_PROCESS_STATUS_WAIT
63
+ #define IO_Event_Selector_process_status_wait(pid) rb_process_status_wait(pid)
64
+ #else
65
+ VALUE IO_Event_Selector_process_status_wait(rb_pid_t pid);
66
+ #endif
67
+
68
+ int IO_Event_Selector_nonblock_set(int file_descriptor);
69
+ void IO_Event_Selector_nonblock_restore(int file_descriptor, int flags);
70
+
71
+ enum IO_Event_Selector_Queue_Flags {
72
+ IO_EVENT_SELECTOR_QUEUE_FIBER = 1,
73
+ IO_EVENT_SELECTOR_QUEUE_INTERNAL = 2,
74
+ };
75
+
76
+ struct IO_Event_Selector_Queue {
77
+ struct IO_Event_Selector_Queue *behind;
78
+ struct IO_Event_Selector_Queue *infront;
79
+
80
+ enum IO_Event_Selector_Queue_Flags flags;
81
+
82
+ VALUE fiber;
83
+ };
84
+
85
+ struct IO_Event_Selector {
86
+ VALUE loop;
87
+
88
+ struct IO_Event_Selector_Queue *free;
89
+
90
+ // Append to waiting.
91
+ struct IO_Event_Selector_Queue *waiting;
92
+ // Process from ready.
93
+ struct IO_Event_Selector_Queue *ready;
94
+ };
95
+
96
+ static inline
97
+ void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE loop) {
98
+ backend->loop = loop;
99
+ backend->waiting = NULL;
100
+ backend->ready = NULL;
101
+ }
102
+
103
+ static inline
104
+ void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
105
+ rb_gc_mark(backend->loop);
106
+
107
+ struct IO_Event_Selector_Queue *ready = backend->ready;
108
+ while (ready) {
109
+ rb_gc_mark(ready->fiber);
110
+ ready = ready->behind;
111
+ }
112
+ }
113
+
114
+ VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALUE *argv);
115
+ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv);
116
+
117
+ static inline
118
+ VALUE IO_Event_Selector_yield(struct IO_Event_Selector *backend)
119
+ {
120
+ return IO_Event_Selector_resume(backend, 1, &backend->loop);
121
+ }
122
+
123
+ void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber);
124
+ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend);
125
+
126
+ void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration);
127
+ void IO_Event_Selector_current_time(struct timespec *time);
128
+
129
+ #define PRINTF_TIMESPEC "%lld.%.9ld"
130
+ #define PRINTF_TIMESPEC_ARGS(ts) (long long)((ts).tv_sec), (ts).tv_nsec