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.
@@ -1,22 +1,5 @@
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.
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
- // 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.
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
- // 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.
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 IO_Event_Selector_fiber_transfer(fiber, argc, argv);
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 = rb_fiber_current()
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 IO_Event_Selector_fiber_raise(fiber, argc, argv);
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 = rb_fiber_current()
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 IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber)
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 IO_Event_Selector_queue_pop(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *ready)
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, "IO_Event_Selector_queue_pop -> %p\n", (void*)ready->fiber);
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 & IO_EVENT_SELECTOR_QUEUE_FIBER) {
305
- IO_Event_Selector_fiber_transfer(ready->fiber, 0, NULL);
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
- if (RTEST(rb_funcall(fiber, id_alive_p, 0))) {
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 IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
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, "IO_Event_Selector_queue_flush waiting = %p\n", waiting);
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
- IO_Event_Selector_queue_pop(backend, ready);
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
- // 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.
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
- static inline
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
- void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber);
150
- int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend);
151
-
152
- void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration);
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
- #define IO_EVENT_PRINTF_TIMESPEC "%lld.%.9ld"
156
- #define IO_EVENT_PRINTF_TIMESPEC_ARGUMENTS(ts) (long long)((ts).tv_sec), (ts).tv_nsec
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
- // 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.
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 = "IO_Event::Backend::URing",
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 = IO_Event_Array_allocate(&selector->completions, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_URing_Completion));
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:IO_Event_Array_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 IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_queue_push(&selector->backend, fiber);
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
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(waiting->fiber, 0, NULL);
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 = IO_Event_Selector_queue_flush(&selector->backend);
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
- IO_Event_Selector_current_time(&start_time);
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
- IO_Event_Selector_current_time(&end_time);
1152
- IO_Event_Selector_elapsed_time(&start_time, &end_time, &selector->idle_duration);
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
- // 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.
1
+ // Released under the MIT License.
2
+ // Copyright, 2021-2025, by Samuel Williams.
20
3
 
21
4
  #pragma once
22
5
 
@@ -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
+ }
@@ -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)