io-event 1.8.4 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/extconf.rb +2 -2
- data/ext/io/event/array.h +16 -0
- data/ext/io/event/event.c +5 -2
- data/ext/io/event/fiber.c +63 -0
- data/ext/io/event/fiber.h +23 -0
- data/ext/io/event/profiler.c +505 -0
- data/ext/io/event/profiler.h +8 -0
- data/ext/io/event/selector/epoll.c +5 -5
- data/ext/io/event/selector/kqueue.c +5 -5
- data/ext/io/event/selector/selector.c +23 -104
- data/ext/io/event/selector/selector.h +38 -19
- data/ext/io/event/selector/uring.c +7 -7
- data/ext/io/event/time.c +10 -3
- data/ext/io/event/time.h +4 -3
- data/lib/io/event/native.rb +11 -0
- data/lib/io/event/profiler.rb +18 -0
- data/lib/io/event/version.rb +2 -2
- data/lib/io/event.rb +2 -8
- data/license.md +1 -1
- data/readme.md +4 -0
- data/releases.md +26 -0
- data.tar.gz.sig +0 -0
- metadata +10 -4
- metadata.gz.sig +0 -0
- data/ext/io/event/profile.c +0 -245
- data/ext/io/event/profile.h +0 -63
@@ -157,7 +157,7 @@ size_t IO_Event_Selector_EPoll_Type_size(const void *_selector)
|
|
157
157
|
}
|
158
158
|
|
159
159
|
static const rb_data_type_t IO_Event_Selector_EPoll_Type = {
|
160
|
-
.wrap_struct_name = "
|
160
|
+
.wrap_struct_name = "IO::Event::Backend::EPoll",
|
161
161
|
.function = {
|
162
162
|
.dmark = IO_Event_Selector_EPoll_Type_mark,
|
163
163
|
.dcompact = IO_Event_Selector_EPoll_Type_compact,
|
@@ -394,7 +394,7 @@ VALUE IO_Event_Selector_EPoll_transfer(VALUE self)
|
|
394
394
|
struct IO_Event_Selector_EPoll *selector = NULL;
|
395
395
|
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
|
396
396
|
|
397
|
-
return
|
397
|
+
return IO_Event_Selector_loop_yield(&selector->backend);
|
398
398
|
}
|
399
399
|
|
400
400
|
VALUE IO_Event_Selector_EPoll_resume(int argc, VALUE *argv, VALUE self)
|
@@ -450,7 +450,7 @@ static
|
|
450
450
|
VALUE process_wait_transfer(VALUE _arguments) {
|
451
451
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
452
452
|
|
453
|
-
|
453
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
454
454
|
|
455
455
|
if (arguments->waiting->ready) {
|
456
456
|
return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
|
@@ -538,7 +538,7 @@ static
|
|
538
538
|
VALUE io_wait_transfer(VALUE _arguments) {
|
539
539
|
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
540
540
|
|
541
|
-
|
541
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
542
542
|
|
543
543
|
if (arguments->waiting->ready) {
|
544
544
|
return RB_INT2NUM(arguments->waiting->ready);
|
@@ -925,7 +925,7 @@ int IO_Event_Selector_EPoll_handle(struct IO_Event_Selector_EPoll *selector, con
|
|
925
925
|
|
926
926
|
// Resume the fiber:
|
927
927
|
waiting->ready = matching_events;
|
928
|
-
|
928
|
+
IO_Event_Selector_loop_resume(&selector->backend, waiting->fiber, 0, NULL);
|
929
929
|
|
930
930
|
node = saved->tail;
|
931
931
|
IO_Event_List_pop(saved);
|
@@ -156,7 +156,7 @@ size_t IO_Event_Selector_KQueue_Type_size(const void *_selector)
|
|
156
156
|
}
|
157
157
|
|
158
158
|
static const rb_data_type_t IO_Event_Selector_KQueue_Type = {
|
159
|
-
.wrap_struct_name = "
|
159
|
+
.wrap_struct_name = "IO::Event::Backend::KQueue",
|
160
160
|
.function = {
|
161
161
|
.dmark = IO_Event_Selector_KQueue_Type_mark,
|
162
162
|
.dcompact = IO_Event_Selector_KQueue_Type_compact,
|
@@ -381,7 +381,7 @@ VALUE IO_Event_Selector_KQueue_transfer(VALUE self)
|
|
381
381
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
382
382
|
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
383
383
|
|
384
|
-
return
|
384
|
+
return IO_Event_Selector_loop_yield(&selector->backend);
|
385
385
|
}
|
386
386
|
|
387
387
|
VALUE IO_Event_Selector_KQueue_resume(int argc, VALUE *argv, VALUE self)
|
@@ -455,7 +455,7 @@ static
|
|
455
455
|
VALUE process_wait_transfer(VALUE _arguments) {
|
456
456
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
457
457
|
|
458
|
-
|
458
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
459
459
|
|
460
460
|
if (arguments->waiting->ready) {
|
461
461
|
process_prewait(arguments->pid);
|
@@ -531,7 +531,7 @@ static
|
|
531
531
|
VALUE io_wait_transfer(VALUE _arguments) {
|
532
532
|
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
533
533
|
|
534
|
-
|
534
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
535
535
|
|
536
536
|
if (arguments->waiting->ready) {
|
537
537
|
return RB_INT2NUM(arguments->waiting->ready);
|
@@ -897,7 +897,7 @@ int IO_Event_Selector_KQueue_handle(struct IO_Event_Selector_KQueue *selector, u
|
|
897
897
|
IO_Event_List_append(node, saved);
|
898
898
|
|
899
899
|
waiting->ready = matching_events;
|
900
|
-
|
900
|
+
IO_Event_Selector_loop_resume(&selector->backend, waiting->fiber, 0, NULL);
|
901
901
|
|
902
902
|
node = saved->tail;
|
903
903
|
IO_Event_List_pop(saved);
|
@@ -2,76 +2,12 @@
|
|
2
2
|
// Copyright, 2021-2025, by Samuel Williams.
|
3
3
|
|
4
4
|
#include "selector.h"
|
5
|
-
#include "../profile.h"
|
6
5
|
|
7
6
|
#include <fcntl.h>
|
8
7
|
#include <stdlib.h>
|
9
8
|
|
10
9
|
static const int DEBUG = 0;
|
11
10
|
|
12
|
-
static ID id_transfer, id_alive_p;
|
13
|
-
|
14
|
-
static float IO_Event_Selector_stall_log_threshold = 0;
|
15
|
-
static int IO_Event_Selector_stall_log_profile = 0;
|
16
|
-
|
17
|
-
VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv) {
|
18
|
-
// TODO Consider introducing something like `rb_fiber_scheduler_transfer(...)`.
|
19
|
-
#ifdef HAVE__RB_FIBER_TRANSFER
|
20
|
-
if (RTEST(rb_obj_is_fiber(fiber))) {
|
21
|
-
if (RTEST(rb_fiber_alive_p(fiber))) {
|
22
|
-
return rb_fiber_transfer(fiber, argc, argv);
|
23
|
-
}
|
24
|
-
|
25
|
-
return Qnil;
|
26
|
-
}
|
27
|
-
#endif
|
28
|
-
if (RTEST(rb_funcall(fiber, id_alive_p, 0))) {
|
29
|
-
return rb_funcallv(fiber, id_transfer, argc, argv);
|
30
|
-
}
|
31
|
-
|
32
|
-
return Qnil;
|
33
|
-
}
|
34
|
-
|
35
|
-
VALUE IO_Event_Selector_fiber_transfer_user(VALUE fiber, int argc, VALUE *argv) {
|
36
|
-
// Bypass if the threshold is not set:
|
37
|
-
if (IO_Event_Selector_stall_log_threshold == 0) {
|
38
|
-
return IO_Event_Selector_fiber_transfer(fiber, argc, argv);
|
39
|
-
}
|
40
|
-
|
41
|
-
VALUE profile = IO_Event_Profile_allocate(IO_Event_Profile);
|
42
|
-
|
43
|
-
IO_Event_Profile_start(profile, IO_Event_Selector_stall_log_profile);
|
44
|
-
|
45
|
-
// Transfer control to the fiber:
|
46
|
-
VALUE result = IO_Event_Selector_fiber_transfer(fiber, argc, argv);
|
47
|
-
|
48
|
-
IO_Event_Profile_stop(profile);
|
49
|
-
|
50
|
-
float duration = IO_Event_Profile_duration(profile);
|
51
|
-
if (duration > IO_Event_Selector_stall_log_threshold) {
|
52
|
-
IO_Event_Profile_print(profile, stderr);
|
53
|
-
}
|
54
|
-
|
55
|
-
return result;
|
56
|
-
}
|
57
|
-
|
58
|
-
#ifndef HAVE__RB_FIBER_RAISE
|
59
|
-
static ID id_raise;
|
60
|
-
|
61
|
-
VALUE IO_Event_Selector_fiber_raise(VALUE fiber, int argc, VALUE *argv) {
|
62
|
-
return rb_funcallv(fiber, id_raise, argc, argv);
|
63
|
-
}
|
64
|
-
#endif
|
65
|
-
|
66
|
-
#ifndef HAVE_RB_FIBER_CURRENT
|
67
|
-
static ID id_current;
|
68
|
-
|
69
|
-
static VALUE rb_fiber_current() {
|
70
|
-
return rb_funcall(rb_cFiber, id_current, 0);
|
71
|
-
}
|
72
|
-
#endif
|
73
|
-
|
74
|
-
|
75
11
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
76
12
|
static ID id_fileno;
|
77
13
|
|
@@ -148,17 +84,6 @@ static VALUE IO_Event_Selector_nonblock(VALUE class, VALUE io)
|
|
148
84
|
}
|
149
85
|
|
150
86
|
void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
151
|
-
id_transfer = rb_intern("transfer");
|
152
|
-
id_alive_p = rb_intern("alive?");
|
153
|
-
|
154
|
-
#ifndef HAVE__RB_FIBER_RAISE
|
155
|
-
id_raise = rb_intern("raise");
|
156
|
-
#endif
|
157
|
-
|
158
|
-
#ifndef HAVE_RB_FIBER_CURRENT
|
159
|
-
id_current = rb_intern("current");
|
160
|
-
#endif
|
161
|
-
|
162
87
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
163
88
|
id_fileno = rb_intern("fileno");
|
164
89
|
#endif
|
@@ -170,31 +95,25 @@ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
170
95
|
#endif
|
171
96
|
|
172
97
|
rb_define_singleton_method(IO_Event_Selector, "nonblock", IO_Event_Selector_nonblock, 1);
|
98
|
+
}
|
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);
|
173
103
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
if (DEBUG) fprintf(stderr, "IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD = %.3f\n", IO_Event_Selector_stall_log_threshold);
|
189
|
-
}
|
190
|
-
|
191
|
-
char *stall_log_profile = getenv("IO_EVENT_SELECTOR_STALL_LOG_PROFILE");
|
192
|
-
|
193
|
-
if (stall_log_profile) {
|
194
|
-
if (strcmp(stall_log_profile, "true") == 0) {
|
195
|
-
IO_Event_Selector_stall_log_profile = 1;
|
196
|
-
}
|
197
|
-
}
|
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);
|
198
117
|
}
|
199
118
|
|
200
119
|
struct wait_and_transfer_arguments {
|
@@ -248,7 +167,7 @@ static VALUE wait_and_transfer(VALUE _arguments) {
|
|
248
167
|
int argc = arguments->argc - 1;
|
249
168
|
VALUE *argv = arguments->argv + 1;
|
250
169
|
|
251
|
-
return
|
170
|
+
return IO_Event_Selector_loop_resume(arguments->backend, fiber, argc, argv);
|
252
171
|
}
|
253
172
|
|
254
173
|
static VALUE wait_and_transfer_ensure(VALUE _arguments) {
|
@@ -267,7 +186,7 @@ VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALU
|
|
267
186
|
.head = NULL,
|
268
187
|
.tail = NULL,
|
269
188
|
.flags = IO_EVENT_SELECTOR_QUEUE_FIBER,
|
270
|
-
.fiber =
|
189
|
+
.fiber = IO_Event_Fiber_current()
|
271
190
|
};
|
272
191
|
|
273
192
|
RB_OBJ_WRITTEN(backend->self, Qundef, waiting.fiber);
|
@@ -291,7 +210,7 @@ static VALUE wait_and_raise(VALUE _arguments) {
|
|
291
210
|
int argc = arguments->argc - 1;
|
292
211
|
VALUE *argv = arguments->argv + 1;
|
293
212
|
|
294
|
-
return
|
213
|
+
return IO_Event_Fiber_raise(fiber, argc, argv);
|
295
214
|
}
|
296
215
|
|
297
216
|
VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv)
|
@@ -302,7 +221,7 @@ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE
|
|
302
221
|
.head = NULL,
|
303
222
|
.tail = NULL,
|
304
223
|
.flags = IO_EVENT_SELECTOR_QUEUE_FIBER,
|
305
|
-
.fiber =
|
224
|
+
.fiber = IO_Event_Fiber_current()
|
306
225
|
};
|
307
226
|
|
308
227
|
RB_OBJ_WRITTEN(backend->self, Qundef, waiting.fiber);
|
@@ -350,7 +269,7 @@ void IO_Event_Selector_ready_pop(struct IO_Event_Selector *backend, struct IO_Ev
|
|
350
269
|
rb_raise(rb_eRuntimeError, "Unknown queue type!");
|
351
270
|
}
|
352
271
|
|
353
|
-
|
272
|
+
IO_Event_Selector_loop_resume(backend, fiber, 0, NULL);
|
354
273
|
}
|
355
274
|
|
356
275
|
int IO_Event_Selector_ready_flush(struct IO_Event_Selector *backend)
|
@@ -8,6 +8,7 @@
|
|
8
8
|
#include <ruby/io.h>
|
9
9
|
|
10
10
|
#include "../time.h"
|
11
|
+
#include "../fiber.h"
|
11
12
|
|
12
13
|
#ifdef HAVE_RUBY_IO_BUFFER_H
|
13
14
|
#include <ruby/io/buffer.h>
|
@@ -39,17 +40,6 @@ static inline int IO_Event_try_again(int error) {
|
|
39
40
|
return error == EAGAIN || error == EWOULDBLOCK;
|
40
41
|
}
|
41
42
|
|
42
|
-
VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv);
|
43
|
-
|
44
|
-
// Specifically for transferring control to a user fiber.
|
45
|
-
VALUE IO_Event_Selector_fiber_transfer_user(VALUE fiber, int argc, VALUE *argv);
|
46
|
-
|
47
|
-
#ifdef HAVE__RB_FIBER_RAISE
|
48
|
-
#define IO_Event_Selector_fiber_raise(fiber, argc, argv) rb_fiber_raise(fiber, argc, argv)
|
49
|
-
#else
|
50
|
-
VALUE IO_Event_Selector_fiber_raise(VALUE fiber, int argc, VALUE *argv);
|
51
|
-
#endif
|
52
|
-
|
53
43
|
#ifdef HAVE_RB_IO_DESCRIPTOR
|
54
44
|
#define IO_Event_Selector_io_descriptor(io) rb_io_descriptor(io)
|
55
45
|
#else
|
@@ -80,23 +70,20 @@ struct IO_Event_Selector_Queue {
|
|
80
70
|
VALUE fiber;
|
81
71
|
};
|
82
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.
|
83
75
|
struct IO_Event_Selector {
|
84
76
|
VALUE self;
|
85
77
|
VALUE loop;
|
86
78
|
|
79
|
+
// The ready queue is a list of fibers that are ready to be resumed from the event loop fiber.
|
87
80
|
// Append to waiting (front/head of queue).
|
88
81
|
struct IO_Event_Selector_Queue *waiting;
|
89
82
|
// Process from ready (back/tail of queue).
|
90
83
|
struct IO_Event_Selector_Queue *ready;
|
91
84
|
};
|
92
85
|
|
93
|
-
|
94
|
-
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop) {
|
95
|
-
RB_OBJ_WRITE(self, &backend->self, self);
|
96
|
-
RB_OBJ_WRITE(self, &backend->loop, loop);
|
97
|
-
backend->waiting = NULL;
|
98
|
-
backend->ready = NULL;
|
99
|
-
}
|
86
|
+
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop);
|
100
87
|
|
101
88
|
static inline
|
102
89
|
void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
|
@@ -123,16 +110,48 @@ void IO_Event_Selector_compact(struct IO_Event_Selector *backend) {
|
|
123
110
|
}
|
124
111
|
}
|
125
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.
|
126
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.
|
127
141
|
VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE *argv);
|
128
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.
|
129
146
|
static inline
|
130
147
|
VALUE IO_Event_Selector_yield(struct IO_Event_Selector *backend)
|
131
148
|
{
|
132
149
|
return IO_Event_Selector_resume(backend, 1, &backend->loop);
|
133
150
|
}
|
134
151
|
|
135
|
-
// Append to the ready queue.
|
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.
|
136
155
|
void IO_Event_Selector_ready_push(struct IO_Event_Selector *backend, VALUE fiber);
|
137
156
|
|
138
157
|
// Flush the ready queue by transferring control one at a time.
|
@@ -127,7 +127,7 @@ size_t IO_Event_Selector_URing_Type_size(const void *_selector)
|
|
127
127
|
}
|
128
128
|
|
129
129
|
static const rb_data_type_t IO_Event_Selector_URing_Type = {
|
130
|
-
.wrap_struct_name = "
|
130
|
+
.wrap_struct_name = "IO::Event::Backend::URing",
|
131
131
|
.function = {
|
132
132
|
.dmark = IO_Event_Selector_URing_Type_mark,
|
133
133
|
.dcompact = IO_Event_Selector_URing_Type_compact,
|
@@ -277,7 +277,7 @@ VALUE IO_Event_Selector_URing_transfer(VALUE self)
|
|
277
277
|
struct IO_Event_Selector_URing *selector = NULL;
|
278
278
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
279
279
|
|
280
|
-
return
|
280
|
+
return IO_Event_Selector_loop_yield(&selector->backend);
|
281
281
|
}
|
282
282
|
|
283
283
|
VALUE IO_Event_Selector_URing_resume(int argc, VALUE *argv, VALUE self)
|
@@ -433,7 +433,7 @@ static
|
|
433
433
|
VALUE process_wait_transfer(VALUE _arguments) {
|
434
434
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
435
435
|
|
436
|
-
|
436
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
437
437
|
|
438
438
|
if (arguments->waiting->result) {
|
439
439
|
return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
|
@@ -548,7 +548,7 @@ VALUE io_wait_transfer(VALUE _arguments) {
|
|
548
548
|
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
549
549
|
struct IO_Event_Selector_URing *selector = arguments->selector;
|
550
550
|
|
551
|
-
|
551
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
552
552
|
|
553
553
|
if (DEBUG) fprintf(stderr, "io_wait_transfer:waiting=%p, result=%d\n", (void*)arguments->waiting, arguments->waiting->result);
|
554
554
|
|
@@ -638,7 +638,7 @@ io_read_submit(VALUE _arguments)
|
|
638
638
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
639
639
|
io_uring_submit_now(selector);
|
640
640
|
|
641
|
-
|
641
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
642
642
|
|
643
643
|
return RB_INT2NUM(arguments->waiting->result);
|
644
644
|
}
|
@@ -802,7 +802,7 @@ io_write_submit(VALUE _argument)
|
|
802
802
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
803
803
|
io_uring_submit_pending(selector);
|
804
804
|
|
805
|
-
|
805
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
806
806
|
|
807
807
|
return RB_INT2NUM(arguments->waiting->result);
|
808
808
|
}
|
@@ -1086,7 +1086,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
1086
1086
|
if (waiting && waiting->fiber) {
|
1087
1087
|
assert(waiting->result != -ECANCELED);
|
1088
1088
|
|
1089
|
-
|
1089
|
+
IO_Event_Selector_loop_resume(&selector->backend, waiting->fiber, 0, NULL);
|
1090
1090
|
}
|
1091
1091
|
}
|
1092
1092
|
|
data/ext/io/event/time.c
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
#include "time.h"
|
5
5
|
|
6
|
-
void IO_Event_Time_elapsed(struct timespec* start, struct timespec* stop, struct timespec *duration)
|
6
|
+
void IO_Event_Time_elapsed(const struct timespec* start, const struct timespec* stop, struct timespec *duration)
|
7
7
|
{
|
8
8
|
if ((stop->tv_nsec - start->tv_nsec) < 0) {
|
9
9
|
duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
|
@@ -14,7 +14,7 @@ void IO_Event_Time_elapsed(struct timespec* start, struct timespec* stop, struct
|
|
14
14
|
}
|
15
15
|
}
|
16
16
|
|
17
|
-
float IO_Event_Time_duration(struct timespec *duration)
|
17
|
+
float IO_Event_Time_duration(const struct timespec *duration)
|
18
18
|
{
|
19
19
|
return duration->tv_sec + duration->tv_nsec / 1000000000.0;
|
20
20
|
}
|
@@ -23,6 +23,13 @@ void IO_Event_Time_current(struct timespec *time) {
|
|
23
23
|
clock_gettime(CLOCK_MONOTONIC, time);
|
24
24
|
}
|
25
25
|
|
26
|
-
float IO_Event_Time_proportion(struct timespec *duration, struct timespec *total_duration) {
|
26
|
+
float IO_Event_Time_proportion(const struct timespec *duration, const struct timespec *total_duration) {
|
27
27
|
return IO_Event_Time_duration(duration) / IO_Event_Time_duration(total_duration);
|
28
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
CHANGED
@@ -6,11 +6,12 @@
|
|
6
6
|
#include <ruby.h>
|
7
7
|
#include <time.h>
|
8
8
|
|
9
|
-
void IO_Event_Time_elapsed(struct timespec* start, struct timespec* stop, struct timespec *duration);
|
10
|
-
float IO_Event_Time_duration(struct timespec *duration);
|
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
11
|
void IO_Event_Time_current(struct timespec *time);
|
12
12
|
|
13
|
-
float
|
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);
|
14
15
|
|
15
16
|
#define IO_EVENT_TIME_PRINTF_TIMESPEC "%.3g"
|
16
17
|
#define IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(ts) ((double)(ts).tv_sec + (ts).tv_nsec / 1e9)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "IO_Event"
|
8
|
+
rescue LoadError => error
|
9
|
+
warn "Could not load native event selector: #{error}"
|
10
|
+
require_relative "selector/nonblock"
|
11
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative "native"
|
7
|
+
|
8
|
+
module IO::Event
|
9
|
+
unless self.const_defined?(:Profiler)
|
10
|
+
module Profiler
|
11
|
+
# The default profiler, if the platform supports it.
|
12
|
+
# Use `IO_EVENT_PROFILER=true` to enable it.
|
13
|
+
def self.default
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/io/event/version.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2021-
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
# @namespace
|
7
7
|
class IO
|
8
8
|
# @namespace
|
9
9
|
module Event
|
10
|
-
VERSION = "1.
|
10
|
+
VERSION = "1.9.0"
|
11
11
|
end
|
12
12
|
end
|
data/lib/io/event.rb
CHANGED
@@ -1,15 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2021-
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative "event/version"
|
7
7
|
require_relative "event/selector"
|
8
8
|
require_relative "event/timers"
|
9
|
-
|
10
|
-
begin
|
11
|
-
require "IO_Event"
|
12
|
-
rescue LoadError => error
|
13
|
-
warn "Could not load native event selector: #{error}"
|
14
|
-
require_relative "event/selector/nonblock"
|
15
|
-
end
|
9
|
+
require_relative "event/native"
|
data/license.md
CHANGED
data/readme.md
CHANGED
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
|
|
18
18
|
|
19
19
|
Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
|
20
20
|
|
21
|
+
### v1.9.0
|
22
|
+
|
23
|
+
- [Improved `IO::Event::Profiler` for detecting stalls.](https://socketry.github.io/io-event/releases/index#improved-io::event::profiler-for-detecting-stalls.)
|
24
|
+
|
21
25
|
### v1.8.0
|
22
26
|
|
23
27
|
- [Detecting fibers that are stalling the event loop.](https://socketry.github.io/io-event/releases/index#detecting-fibers-that-are-stalling-the-event-loop.)
|
data/releases.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v1.9.0
|
4
|
+
|
5
|
+
### Improved `IO::Event::Profiler` for detecting stalls.
|
6
|
+
|
7
|
+
A new `IO::Event::Profiler` class has been added to help detect stalls in the event loop. The previous approach was insufficient to detect all possible stalls. This new approach uses the `RUBY_EVENT_FIBER_SWITCH` event to track context switching by the scheduler, and can detect stalls no matter how they occur.
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
profiler = IO::Event::Profiler.new
|
11
|
+
|
12
|
+
profiler.start
|
13
|
+
|
14
|
+
Fiber.new do
|
15
|
+
sleep 1.0
|
16
|
+
end.transfer
|
17
|
+
|
18
|
+
profiler.stop
|
19
|
+
```
|
20
|
+
|
21
|
+
A default profiler is exposed using `IO::Event::Profiler.default` which is controlled by the following environment variables:
|
22
|
+
|
23
|
+
- `IO_EVENT_PROFILER=true` - Enable the profiler, otherwise `IO::Event::Profiler.default` will return `nil`.
|
24
|
+
- `IO_EVENT_PROFILER_LOG_THRESHOLD` - Specify the threshold in seconds for logging a stall. Defaults to `0.01`.
|
25
|
+
- `IO_EVENT_PROFILER_TRACK_CALLS` - Track the method call for each event, in order to log specifically which method is causing the stall. Defaults to `true`.
|
26
|
+
|
27
|
+
The previous environment variables `IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD` and `IO_EVENT_SELECTOR_STALL_LOG` no longer have any effect.
|
28
|
+
|
3
29
|
## v1.8.0
|
4
30
|
|
5
31
|
### Detecting fibers that are stalling the event loop.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-event
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
- Math Ieu
|
9
9
|
- Wander Hillen
|
10
|
+
- Jean Boussier
|
10
11
|
- Benoit Daloze
|
11
12
|
- Bruno Sutic
|
12
13
|
- Alex Matchneer
|
13
14
|
- Anthony Ross
|
14
15
|
- Delton Ding
|
15
16
|
- Pavel Rosický
|
17
|
+
- Shizuo Fujita
|
16
18
|
bindir: bin
|
17
19
|
cert_chain:
|
18
20
|
- |
|
@@ -44,7 +46,7 @@ cert_chain:
|
|
44
46
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
45
47
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
46
48
|
-----END CERTIFICATE-----
|
47
|
-
date: 2025-02-
|
49
|
+
date: 2025-02-10 00:00:00.000000000 Z
|
48
50
|
dependencies: []
|
49
51
|
executables: []
|
50
52
|
extensions:
|
@@ -56,11 +58,13 @@ files:
|
|
56
58
|
- ext/io/event/array.h
|
57
59
|
- ext/io/event/event.c
|
58
60
|
- ext/io/event/event.h
|
61
|
+
- ext/io/event/fiber.c
|
62
|
+
- ext/io/event/fiber.h
|
59
63
|
- ext/io/event/interrupt.c
|
60
64
|
- ext/io/event/interrupt.h
|
61
65
|
- ext/io/event/list.h
|
62
|
-
- ext/io/event/
|
63
|
-
- ext/io/event/
|
66
|
+
- ext/io/event/profiler.c
|
67
|
+
- ext/io/event/profiler.h
|
64
68
|
- ext/io/event/selector/epoll.c
|
65
69
|
- ext/io/event/selector/epoll.h
|
66
70
|
- ext/io/event/selector/kqueue.c
|
@@ -75,7 +79,9 @@ files:
|
|
75
79
|
- lib/io/event.rb
|
76
80
|
- lib/io/event/debug/selector.rb
|
77
81
|
- lib/io/event/interrupt.rb
|
82
|
+
- lib/io/event/native.rb
|
78
83
|
- lib/io/event/priority_heap.rb
|
84
|
+
- lib/io/event/profiler.rb
|
79
85
|
- lib/io/event/selector.rb
|
80
86
|
- lib/io/event/selector/nonblock.rb
|
81
87
|
- lib/io/event/selector/select.rb
|
metadata.gz.sig
CHANGED
Binary file
|