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.
@@ -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 = "IO_Event::Backend::EPoll",
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 IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer_user(waiting->fiber, 0, NULL);
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 = "IO_Event::Backend::KQueue",
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 IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer_user(waiting->fiber, 0, NULL);
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
- // Extract the stall log threshold if specified:
175
-
176
- char *stall_log_threshold = getenv("IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD");
177
- // Can be true, false or a floating point time in seconds:
178
-
179
- if (stall_log_threshold) {
180
- if (strcmp(stall_log_threshold, "true") == 0) {
181
- IO_Event_Selector_stall_log_threshold = 0.01;
182
- } else if (strcmp(stall_log_threshold, "false") == 0) {
183
- IO_Event_Selector_stall_log_threshold = 0;
184
- } else {
185
- IO_Event_Selector_stall_log_threshold = strtof(stall_log_threshold, NULL);
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 IO_Event_Selector_fiber_transfer_user(fiber, argc, argv);
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 = rb_fiber_current()
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 IO_Event_Selector_fiber_raise(fiber, argc, argv);
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 = rb_fiber_current()
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
- IO_Event_Selector_fiber_transfer_user(fiber, 0, NULL);
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
- static inline
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 = "IO_Event::Backend::URing",
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 IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
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
- IO_Event_Selector_fiber_transfer_user(waiting->fiber, 0, NULL);
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 IO_Event_Time_proportion(struct timespec *duration, struct timespec *total_duration);
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
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
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.8.4"
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-2024, by Samuel Williams.
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
@@ -1,7 +1,7 @@
1
1
  # MIT License
2
2
 
3
3
  Copyright, 2021, by Wander Hillen.
4
- Copyright, 2021-2024, by Samuel Williams.
4
+ Copyright, 2021-2025, by Samuel Williams.
5
5
  Copyright, 2021, by Delton Ding.
6
6
  Copyright, 2021-2024, by Benoit Daloze.
7
7
  Copyright, 2022, by Alex Matchneer.
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.8.4
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-05 00:00:00.000000000 Z
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/profile.c
63
- - ext/io/event/profile.h
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