io-event 1.7.4 → 1.10.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/extconf.rb +2 -2
- data/ext/io/event/{selector/array.h → array.h} +23 -1
- data/ext/io/event/event.c +6 -20
- data/ext/io/event/event.h +2 -19
- data/ext/io/event/fiber.c +63 -0
- data/ext/io/event/fiber.h +23 -0
- data/ext/io/event/interrupt.c +12 -27
- data/ext/io/event/interrupt.h +2 -19
- data/ext/io/event/{selector/list.h → list.h} +1 -1
- data/ext/io/event/selector/epoll.c +36 -35
- data/ext/io/event/selector/epoll.h +2 -19
- data/ext/io/event/selector/kqueue.c +37 -35
- data/ext/io/event/selector/kqueue.h +2 -19
- data/ext/io/event/selector/pidfd.c +2 -19
- data/ext/io/event/selector/selector.c +42 -101
- data/ext/io/event/selector/selector.h +45 -43
- data/ext/io/event/selector/uring.c +37 -35
- data/ext/io/event/selector/uring.h +2 -19
- data/ext/io/event/time.c +35 -0
- data/ext/io/event/time.h +17 -0
- data/lib/io/event/debug/selector.rb +41 -1
- data/lib/io/event/native.rb +11 -0
- data/lib/io/event/priority_heap.rb +13 -14
- data/lib/io/event/selector/nonblock.rb +4 -0
- data/lib/io/event/selector/select.rb +19 -3
- data/lib/io/event/selector.rb +10 -0
- data/lib/io/event/support.rb +12 -0
- data/lib/io/event/timers.rb +41 -3
- data/lib/io/event/version.rb +4 -2
- data/lib/io/event.rb +2 -8
- data/license.md +1 -1
- data/readme.md +25 -2
- data/releases.md +18 -0
- data.tar.gz.sig +0 -0
- metadata +13 -10
- metadata.gz.sig +0 -0
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include <ruby.h>
|
22
5
|
|
@@ -1,65 +1,13 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "selector.h"
|
5
|
+
|
22
6
|
#include <fcntl.h>
|
7
|
+
#include <stdlib.h>
|
23
8
|
|
24
9
|
static const int DEBUG = 0;
|
25
10
|
|
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
11
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
64
12
|
static ID id_fileno;
|
65
13
|
|
@@ -136,17 +84,6 @@ static VALUE IO_Event_Selector_nonblock(VALUE class, VALUE io)
|
|
136
84
|
}
|
137
85
|
|
138
86
|
void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
139
|
-
id_transfer = rb_intern("transfer");
|
140
|
-
id_alive_p = rb_intern("alive?");
|
141
|
-
|
142
|
-
#ifndef HAVE__RB_FIBER_RAISE
|
143
|
-
id_raise = rb_intern("raise");
|
144
|
-
#endif
|
145
|
-
|
146
|
-
#ifndef HAVE_RB_FIBER_CURRENT
|
147
|
-
id_current = rb_intern("current");
|
148
|
-
#endif
|
149
|
-
|
150
87
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
151
88
|
id_fileno = rb_intern("fileno");
|
152
89
|
#endif
|
@@ -156,10 +93,29 @@ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
156
93
|
rb_Process_Status = rb_const_get_at(rb_mProcess, rb_intern("Status"));
|
157
94
|
rb_gc_register_mark_object(rb_Process_Status);
|
158
95
|
#endif
|
159
|
-
|
96
|
+
|
160
97
|
rb_define_singleton_method(IO_Event_Selector, "nonblock", IO_Event_Selector_nonblock, 1);
|
161
98
|
}
|
162
99
|
|
100
|
+
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop) {
|
101
|
+
RB_OBJ_WRITE(self, &backend->self, self);
|
102
|
+
RB_OBJ_WRITE(self, &backend->loop, loop);
|
103
|
+
|
104
|
+
backend->waiting = NULL;
|
105
|
+
backend->ready = NULL;
|
106
|
+
}
|
107
|
+
|
108
|
+
VALUE IO_Event_Selector_loop_resume(struct IO_Event_Selector *backend, VALUE fiber, int argc, VALUE *argv) {
|
109
|
+
return IO_Event_Fiber_transfer(fiber, argc, argv);
|
110
|
+
}
|
111
|
+
|
112
|
+
VALUE IO_Event_Selector_loop_yield(struct IO_Event_Selector *backend)
|
113
|
+
{
|
114
|
+
// TODO Why is this assertion failing in async?
|
115
|
+
// RUBY_ASSERT(backend->loop != IO_Event_Fiber_current());
|
116
|
+
return IO_Event_Fiber_transfer(backend->loop, 0, NULL);
|
117
|
+
}
|
118
|
+
|
163
119
|
struct wait_and_transfer_arguments {
|
164
120
|
int argc;
|
165
121
|
VALUE *argv;
|
@@ -211,7 +167,7 @@ static VALUE wait_and_transfer(VALUE _arguments) {
|
|
211
167
|
int argc = arguments->argc - 1;
|
212
168
|
VALUE *argv = arguments->argv + 1;
|
213
169
|
|
214
|
-
return
|
170
|
+
return IO_Event_Selector_loop_resume(arguments->backend, fiber, argc, argv);
|
215
171
|
}
|
216
172
|
|
217
173
|
static VALUE wait_and_transfer_ensure(VALUE _arguments) {
|
@@ -230,7 +186,7 @@ VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALU
|
|
230
186
|
.head = NULL,
|
231
187
|
.tail = NULL,
|
232
188
|
.flags = IO_EVENT_SELECTOR_QUEUE_FIBER,
|
233
|
-
.fiber =
|
189
|
+
.fiber = IO_Event_Fiber_current()
|
234
190
|
};
|
235
191
|
|
236
192
|
RB_OBJ_WRITTEN(backend->self, Qundef, waiting.fiber);
|
@@ -254,7 +210,7 @@ static VALUE wait_and_raise(VALUE _arguments) {
|
|
254
210
|
int argc = arguments->argc - 1;
|
255
211
|
VALUE *argv = arguments->argv + 1;
|
256
212
|
|
257
|
-
return
|
213
|
+
return IO_Event_Fiber_raise(fiber, argc, argv);
|
258
214
|
}
|
259
215
|
|
260
216
|
VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv)
|
@@ -265,7 +221,7 @@ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE
|
|
265
221
|
.head = NULL,
|
266
222
|
.tail = NULL,
|
267
223
|
.flags = IO_EVENT_SELECTOR_QUEUE_FIBER,
|
268
|
-
.fiber =
|
224
|
+
.fiber = IO_Event_Fiber_current()
|
269
225
|
};
|
270
226
|
|
271
227
|
RB_OBJ_WRITTEN(backend->self, Qundef, waiting.fiber);
|
@@ -282,7 +238,7 @@ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE
|
|
282
238
|
return rb_ensure(wait_and_raise, (VALUE)&arguments, wait_and_transfer_ensure, (VALUE)&arguments);
|
283
239
|
}
|
284
240
|
|
285
|
-
void
|
241
|
+
void IO_Event_Selector_ready_push(struct IO_Event_Selector *backend, VALUE fiber)
|
286
242
|
{
|
287
243
|
struct IO_Event_Selector_Queue *waiting = malloc(sizeof(struct IO_Event_Selector_Queue));
|
288
244
|
assert(waiting);
|
@@ -297,26 +253,26 @@ void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber
|
|
297
253
|
}
|
298
254
|
|
299
255
|
static inline
|
300
|
-
void
|
256
|
+
void IO_Event_Selector_ready_pop(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *ready)
|
301
257
|
{
|
302
|
-
if (DEBUG) fprintf(stderr, "
|
258
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_ready_pop -> %p\n", (void*)ready->fiber);
|
259
|
+
|
260
|
+
VALUE fiber = ready->fiber;
|
303
261
|
|
304
|
-
if (ready->flags &
|
305
|
-
|
306
|
-
} else if (ready->flags & IO_EVENT_SELECTOR_QUEUE_INTERNAL) {
|
307
|
-
VALUE fiber = ready->fiber;
|
262
|
+
if (ready->flags & IO_EVENT_SELECTOR_QUEUE_INTERNAL) {
|
263
|
+
// This means that the fiber was added to the ready queue by the selector itself, and we need to transfer control to it, but before we do that, we need to remove it from the queue, as there is no expectation that returning from `transfer` will remove it.
|
308
264
|
queue_pop(backend, ready);
|
309
265
|
free(ready);
|
310
|
-
|
311
|
-
|
312
|
-
rb_funcall(fiber, id_transfer, 0);
|
313
|
-
}
|
266
|
+
} else if (ready->flags & IO_EVENT_SELECTOR_QUEUE_FIBER) {
|
267
|
+
// This means the fiber added itself to the ready queue, and we need to transfer control back to it. Transferring control back to the fiber will call `queue_pop` and remove it from the queue.
|
314
268
|
} else {
|
315
269
|
rb_raise(rb_eRuntimeError, "Unknown queue type!");
|
316
270
|
}
|
271
|
+
|
272
|
+
IO_Event_Selector_loop_resume(backend, fiber, 0, NULL);
|
317
273
|
}
|
318
274
|
|
319
|
-
int
|
275
|
+
int IO_Event_Selector_ready_flush(struct IO_Event_Selector *backend)
|
320
276
|
{
|
321
277
|
int count = 0;
|
322
278
|
|
@@ -324,7 +280,7 @@ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
|
|
324
280
|
|
325
281
|
// Get the current tail and head of the queue:
|
326
282
|
struct IO_Event_Selector_Queue *waiting = backend->waiting;
|
327
|
-
if (DEBUG) fprintf(stderr, "
|
283
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_ready_flush waiting = %p\n", waiting);
|
328
284
|
|
329
285
|
// Process from head to tail in order:
|
330
286
|
// During this, more items may be appended to tail.
|
@@ -333,25 +289,10 @@ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
|
|
333
289
|
struct IO_Event_Selector_Queue *ready = backend->ready;
|
334
290
|
|
335
291
|
count += 1;
|
336
|
-
|
292
|
+
IO_Event_Selector_ready_pop(backend, ready);
|
337
293
|
|
338
294
|
if (ready == waiting) break;
|
339
295
|
}
|
340
296
|
|
341
297
|
return count;
|
342
298
|
}
|
343
|
-
|
344
|
-
void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration)
|
345
|
-
{
|
346
|
-
if ((stop->tv_nsec - start->tv_nsec) < 0) {
|
347
|
-
duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
|
348
|
-
duration->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
|
349
|
-
} else {
|
350
|
-
duration->tv_sec = stop->tv_sec - start->tv_sec;
|
351
|
-
duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
void IO_Event_Selector_current_time(struct timespec *time) {
|
356
|
-
clock_gettime(CLOCK_MONOTONIC, time);
|
357
|
-
}
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
@@ -24,6 +7,9 @@
|
|
24
7
|
#include <ruby/thread.h>
|
25
8
|
#include <ruby/io.h>
|
26
9
|
|
10
|
+
#include "../time.h"
|
11
|
+
#include "../fiber.h"
|
12
|
+
|
27
13
|
#ifdef HAVE_RUBY_IO_BUFFER_H
|
28
14
|
#include <ruby/io/buffer.h>
|
29
15
|
#include <ruby/fiber/scheduler.h>
|
@@ -33,8 +19,6 @@
|
|
33
19
|
#define RUBY_FIBER_SCHEDULER_VERSION 1
|
34
20
|
#endif
|
35
21
|
|
36
|
-
#include <time.h>
|
37
|
-
|
38
22
|
#ifdef HAVE_SYS_WAIT_H
|
39
23
|
#include <sys/wait.h>
|
40
24
|
#endif
|
@@ -56,14 +40,6 @@ static inline int IO_Event_try_again(int error) {
|
|
56
40
|
return error == EAGAIN || error == EWOULDBLOCK;
|
57
41
|
}
|
58
42
|
|
59
|
-
VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv);
|
60
|
-
|
61
|
-
#ifdef HAVE__RB_FIBER_RAISE
|
62
|
-
#define IO_Event_Selector_fiber_raise(fiber, argc, argv) rb_fiber_raise(fiber, argc, argv)
|
63
|
-
#else
|
64
|
-
VALUE IO_Event_Selector_fiber_raise(VALUE fiber, int argc, VALUE *argv);
|
65
|
-
#endif
|
66
|
-
|
67
43
|
#ifdef HAVE_RB_IO_DESCRIPTOR
|
68
44
|
#define IO_Event_Selector_io_descriptor(io) rb_io_descriptor(io)
|
69
45
|
#else
|
@@ -94,23 +70,20 @@ struct IO_Event_Selector_Queue {
|
|
94
70
|
VALUE fiber;
|
95
71
|
};
|
96
72
|
|
73
|
+
// The internal state of the event selector.
|
74
|
+
// The event selector is responsible for managing the scheduling of fibers, as well as selecting for events.
|
97
75
|
struct IO_Event_Selector {
|
98
76
|
VALUE self;
|
99
77
|
VALUE loop;
|
100
78
|
|
79
|
+
// The ready queue is a list of fibers that are ready to be resumed from the event loop fiber.
|
101
80
|
// Append to waiting (front/head of queue).
|
102
81
|
struct IO_Event_Selector_Queue *waiting;
|
103
82
|
// Process from ready (back/tail of queue).
|
104
83
|
struct IO_Event_Selector_Queue *ready;
|
105
84
|
};
|
106
85
|
|
107
|
-
|
108
|
-
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop) {
|
109
|
-
RB_OBJ_WRITE(self, &backend->self, self);
|
110
|
-
RB_OBJ_WRITE(self, &backend->loop, loop);
|
111
|
-
backend->waiting = NULL;
|
112
|
-
backend->ready = NULL;
|
113
|
-
}
|
86
|
+
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop);
|
114
87
|
|
115
88
|
static inline
|
116
89
|
void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
|
@@ -137,20 +110,49 @@ void IO_Event_Selector_compact(struct IO_Event_Selector *backend) {
|
|
137
110
|
}
|
138
111
|
}
|
139
112
|
|
113
|
+
// Transfer control from the event loop to a user fiber.
|
114
|
+
// This is used to transfer control to a user fiber when it may proceed.
|
115
|
+
// Strictly speaking, it's not a scheduling operation (does not schedule the current fiber).
|
116
|
+
VALUE IO_Event_Selector_loop_resume(struct IO_Event_Selector *backend, VALUE fiber, int argc, VALUE *argv);
|
117
|
+
|
118
|
+
// Transfer from a user fiber back to the event loop.
|
119
|
+
// This is used to transfer control back to the event loop in order to wait for events.
|
120
|
+
// Strictly speaking, it's not a scheduling operation (does not schedule the current fiber).
|
121
|
+
VALUE IO_Event_Selector_loop_yield(struct IO_Event_Selector *backend);
|
122
|
+
|
123
|
+
// Resume a specific fiber. This is a scheduling operation.
|
124
|
+
// The first argument is the fiber, the rest are the arguments to the resume.
|
125
|
+
//
|
126
|
+
// The implementation has two possible strategies:
|
127
|
+
// 1. Add the current fiber to the ready queue and transfer control to the target fiber.
|
128
|
+
// 2. Schedule the target fiber to be resumed by the event loop later on.
|
129
|
+
//
|
130
|
+
// We currently only implement the first strategy.
|
140
131
|
VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALUE *argv);
|
132
|
+
|
133
|
+
// Raise an exception on a specific fiber.
|
134
|
+
// The first argument is the fiber, the rest are the arguments to the exception.
|
135
|
+
//
|
136
|
+
// The implementation has two possible strategies:
|
137
|
+
// 1. Add the current fiber to the ready queue and transfer control to the target fiber.
|
138
|
+
// 2. Schedule the target fiber to be resumed by the event loop with an exception later on.
|
139
|
+
//
|
140
|
+
// We currently only implement the first strategy.
|
141
141
|
VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv);
|
142
142
|
|
143
|
+
// Yield control to the event loop. This is a scheduling operation.
|
144
|
+
//
|
145
|
+
// The implementation adds the current fiber to the ready queue and transfers control to the event loop.
|
143
146
|
static inline
|
144
147
|
VALUE IO_Event_Selector_yield(struct IO_Event_Selector *backend)
|
145
148
|
{
|
146
149
|
return IO_Event_Selector_resume(backend, 1, &backend->loop);
|
147
150
|
}
|
148
151
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
void
|
153
|
-
void IO_Event_Selector_current_time(struct timespec *time);
|
152
|
+
// Append a specific fiber to the ready queue.
|
153
|
+
// The fiber can be an actual fiber or an object that responds to `alive?` and `transfer`.
|
154
|
+
// The implementation will transfer control to the fiber later on.
|
155
|
+
void IO_Event_Selector_ready_push(struct IO_Event_Selector *backend, VALUE fiber);
|
154
156
|
|
155
|
-
|
156
|
-
|
157
|
+
// Flush the ready queue by transferring control one at a time.
|
158
|
+
int IO_Event_Selector_ready_flush(struct IO_Event_Selector *backend);
|
@@ -1,27 +1,10 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "uring.h"
|
22
5
|
#include "selector.h"
|
23
|
-
#include "list.h"
|
24
|
-
#include "array.h"
|
6
|
+
#include "../list.h"
|
7
|
+
#include "../array.h"
|
25
8
|
|
26
9
|
#include <liburing.h>
|
27
10
|
#include <poll.h>
|
@@ -144,7 +127,7 @@ size_t IO_Event_Selector_URing_Type_size(const void *_selector)
|
|
144
127
|
}
|
145
128
|
|
146
129
|
static const rb_data_type_t IO_Event_Selector_URing_Type = {
|
147
|
-
.wrap_struct_name = "
|
130
|
+
.wrap_struct_name = "IO::Event::Backend::URing",
|
148
131
|
.function = {
|
149
132
|
.dmark = IO_Event_Selector_URing_Type_mark,
|
150
133
|
.dcompact = IO_Event_Selector_URing_Type_compact,
|
@@ -238,9 +221,9 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
238
221
|
|
239
222
|
selector->completions.element_initialize = IO_Event_Selector_URing_Completion_initialize;
|
240
223
|
selector->completions.element_free = IO_Event_Selector_URing_Completion_free;
|
241
|
-
int result =
|
224
|
+
int result = IO_Event_Array_initialize(&selector->completions, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_URing_Completion));
|
242
225
|
if (result < 0) {
|
243
|
-
rb_sys_fail("IO_Event_Selector_URing_allocate:
|
226
|
+
rb_sys_fail("IO_Event_Selector_URing_allocate:IO_Event_Array_initialize");
|
244
227
|
}
|
245
228
|
|
246
229
|
return instance;
|
@@ -294,7 +277,7 @@ VALUE IO_Event_Selector_URing_transfer(VALUE self)
|
|
294
277
|
struct IO_Event_Selector_URing *selector = NULL;
|
295
278
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
296
279
|
|
297
|
-
return
|
280
|
+
return IO_Event_Selector_loop_yield(&selector->backend);
|
298
281
|
}
|
299
282
|
|
300
283
|
VALUE IO_Event_Selector_URing_resume(int argc, VALUE *argv, VALUE self)
|
@@ -318,7 +301,7 @@ VALUE IO_Event_Selector_URing_push(VALUE self, VALUE fiber)
|
|
318
301
|
struct IO_Event_Selector_URing *selector = NULL;
|
319
302
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
320
303
|
|
321
|
-
|
304
|
+
IO_Event_Selector_ready_push(&selector->backend, fiber);
|
322
305
|
|
323
306
|
return Qnil;
|
324
307
|
}
|
@@ -450,7 +433,7 @@ static
|
|
450
433
|
VALUE process_wait_transfer(VALUE _arguments) {
|
451
434
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
452
435
|
|
453
|
-
|
436
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
454
437
|
|
455
438
|
if (arguments->waiting->result) {
|
456
439
|
return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
|
@@ -565,7 +548,7 @@ VALUE io_wait_transfer(VALUE _arguments) {
|
|
565
548
|
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
566
549
|
struct IO_Event_Selector_URing *selector = arguments->selector;
|
567
550
|
|
568
|
-
|
551
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
569
552
|
|
570
553
|
if (DEBUG) fprintf(stderr, "io_wait_transfer:waiting=%p, result=%d\n", (void*)arguments->waiting, arguments->waiting->result);
|
571
554
|
|
@@ -655,7 +638,7 @@ io_read_submit(VALUE _arguments)
|
|
655
638
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
656
639
|
io_uring_submit_now(selector);
|
657
640
|
|
658
|
-
|
641
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
659
642
|
|
660
643
|
return RB_INT2NUM(arguments->waiting->result);
|
661
644
|
}
|
@@ -819,7 +802,7 @@ io_write_submit(VALUE _argument)
|
|
819
802
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
820
803
|
io_uring_submit_pending(selector);
|
821
804
|
|
822
|
-
|
805
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
823
806
|
|
824
807
|
return RB_INT2NUM(arguments->waiting->result);
|
825
808
|
}
|
@@ -1103,7 +1086,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
1103
1086
|
if (waiting && waiting->fiber) {
|
1104
1087
|
assert(waiting->result != -ECANCELED);
|
1105
1088
|
|
1106
|
-
|
1089
|
+
IO_Event_Selector_loop_resume(&selector->backend, waiting->fiber, 0, NULL);
|
1107
1090
|
}
|
1108
1091
|
}
|
1109
1092
|
|
@@ -1122,7 +1105,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
1122
1105
|
// Flush any pending events:
|
1123
1106
|
io_uring_submit_flush(selector);
|
1124
1107
|
|
1125
|
-
int ready =
|
1108
|
+
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
1126
1109
|
|
1127
1110
|
int result = select_process_completions(selector);
|
1128
1111
|
|
@@ -1142,14 +1125,14 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
1142
1125
|
|
1143
1126
|
if (!selector->backend.ready && !timeout_nonblocking(arguments.timeout)) {
|
1144
1127
|
struct timespec start_time;
|
1145
|
-
|
1128
|
+
IO_Event_Time_current(&start_time);
|
1146
1129
|
|
1147
1130
|
// This is a blocking operation, we wait for events:
|
1148
1131
|
result = select_internal_without_gvl(&arguments);
|
1149
1132
|
|
1150
1133
|
struct timespec end_time;
|
1151
|
-
|
1152
|
-
|
1134
|
+
IO_Event_Time_current(&end_time);
|
1135
|
+
IO_Event_Time_elapsed(&start_time, &end_time, &selector->idle_duration);
|
1153
1136
|
|
1154
1137
|
// After waiting/flushing the SQ, check if there are any completions:
|
1155
1138
|
if (result > 0) {
|
@@ -1192,7 +1175,26 @@ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
|
1192
1175
|
|
1193
1176
|
#pragma mark - Native Methods
|
1194
1177
|
|
1178
|
+
static int IO_Event_Selector_URing_supported_p(void) {
|
1179
|
+
struct io_uring ring;
|
1180
|
+
int result = io_uring_queue_init(32, &ring, 0);
|
1181
|
+
|
1182
|
+
if (result < 0) {
|
1183
|
+
rb_warn("io_uring_queue_init() was available at compile time but failed at run time: %s\n", strerror(-result));
|
1184
|
+
|
1185
|
+
return 0;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
io_uring_queue_exit(&ring);
|
1189
|
+
|
1190
|
+
return 1;
|
1191
|
+
}
|
1192
|
+
|
1195
1193
|
void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
1194
|
+
if (!IO_Event_Selector_URing_supported_p()) {
|
1195
|
+
return;
|
1196
|
+
}
|
1197
|
+
|
1196
1198
|
VALUE IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
|
1197
1199
|
|
1198
1200
|
rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
data/ext/io/event/time.c
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
3
|
+
|
4
|
+
#include "time.h"
|
5
|
+
|
6
|
+
void IO_Event_Time_elapsed(const struct timespec* start, const struct timespec* stop, struct timespec *duration)
|
7
|
+
{
|
8
|
+
if ((stop->tv_nsec - start->tv_nsec) < 0) {
|
9
|
+
duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
|
10
|
+
duration->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
|
11
|
+
} else {
|
12
|
+
duration->tv_sec = stop->tv_sec - start->tv_sec;
|
13
|
+
duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
float IO_Event_Time_duration(const struct timespec *duration)
|
18
|
+
{
|
19
|
+
return duration->tv_sec + duration->tv_nsec / 1000000000.0;
|
20
|
+
}
|
21
|
+
|
22
|
+
void IO_Event_Time_current(struct timespec *time) {
|
23
|
+
clock_gettime(CLOCK_MONOTONIC, time);
|
24
|
+
}
|
25
|
+
|
26
|
+
float IO_Event_Time_proportion(const struct timespec *duration, const struct timespec *total_duration) {
|
27
|
+
return IO_Event_Time_duration(duration) / IO_Event_Time_duration(total_duration);
|
28
|
+
}
|
29
|
+
|
30
|
+
float IO_Event_Time_delta(const struct timespec *start, const struct timespec *stop) {
|
31
|
+
struct timespec duration;
|
32
|
+
IO_Event_Time_elapsed(start, stop, &duration);
|
33
|
+
|
34
|
+
return IO_Event_Time_duration(&duration);
|
35
|
+
}
|
data/ext/io/event/time.h
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
3
|
+
|
4
|
+
#pragma once
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
#include <time.h>
|
8
|
+
|
9
|
+
void IO_Event_Time_elapsed(const struct timespec* start, const struct timespec* stop, struct timespec *duration);
|
10
|
+
float IO_Event_Time_duration(const struct timespec *duration);
|
11
|
+
void IO_Event_Time_current(struct timespec *time);
|
12
|
+
|
13
|
+
float IO_Event_Time_delta(const struct timespec *start, const struct timespec *stop);
|
14
|
+
float IO_Event_Time_proportion(const struct timespec *duration, const struct timespec *total_duration);
|
15
|
+
|
16
|
+
#define IO_EVENT_TIME_PRINTF_TIMESPEC "%.3g"
|
17
|
+
#define IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(ts) ((double)(ts).tv_sec + (ts).tv_nsec / 1e9)
|