io-event 1.6.5 → 1.9.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ext/extconf.rb +26 -13
  4. data/ext/io/event/{selector/array.h → array.h} +74 -18
  5. data/ext/io/event/event.c +9 -26
  6. data/ext/io/event/event.h +2 -19
  7. data/ext/io/event/fiber.c +63 -0
  8. data/ext/io/event/fiber.h +23 -0
  9. data/ext/io/event/interrupt.c +19 -27
  10. data/ext/io/event/interrupt.h +2 -19
  11. data/ext/io/event/{selector/list.h → list.h} +20 -2
  12. data/ext/io/event/profiler.c +505 -0
  13. data/ext/io/event/profiler.h +8 -0
  14. data/ext/io/event/selector/epoll.c +41 -43
  15. data/ext/io/event/selector/epoll.h +2 -19
  16. data/ext/io/event/selector/kqueue.c +34 -41
  17. data/ext/io/event/selector/kqueue.h +2 -19
  18. data/ext/io/event/selector/pidfd.c +2 -19
  19. data/ext/io/event/selector/selector.c +65 -102
  20. data/ext/io/event/selector/selector.h +51 -46
  21. data/ext/io/event/selector/uring.c +130 -47
  22. data/ext/io/event/selector/uring.h +2 -19
  23. data/ext/io/event/time.c +35 -0
  24. data/ext/io/event/time.h +17 -0
  25. data/lib/io/event/debug/selector.rb +44 -4
  26. data/lib/io/event/interrupt.rb +2 -2
  27. data/lib/io/event/native.rb +11 -0
  28. data/lib/io/event/priority_heap.rb +13 -14
  29. data/lib/io/event/profiler.rb +18 -0
  30. data/lib/io/event/selector/nonblock.rb +6 -2
  31. data/lib/io/event/selector/select.rb +25 -5
  32. data/lib/io/event/selector.rb +15 -5
  33. data/lib/io/event/support.rb +23 -2
  34. data/lib/io/event/timers.rb +42 -4
  35. data/lib/io/event/version.rb +4 -2
  36. data/lib/io/event.rb +5 -11
  37. data/license.md +4 -1
  38. data/readme.md +22 -4
  39. data/releases.md +57 -0
  40. data.tar.gz.sig +0 -0
  41. metadata +20 -11
  42. metadata.gz.sig +0 -0
@@ -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>
@@ -35,10 +18,9 @@
35
18
  enum {
36
19
  DEBUG = 0,
37
20
  DEBUG_COMPLETION = 0,
21
+ DEBUG_IO_READ = 1,
38
22
  };
39
23
 
40
- static VALUE IO_Event_Selector_URing = Qnil;
41
-
42
24
  enum {URING_ENTRIES = 64};
43
25
 
44
26
  #pragma mark - Data Type
@@ -140,11 +122,12 @@ size_t IO_Event_Selector_URing_Type_size(const void *_selector)
140
122
 
141
123
  return sizeof(struct IO_Event_Selector_URing)
142
124
  + IO_Event_Array_memory_size(&selector->completions)
125
+ + IO_Event_List_memory_size(&selector->free_list)
143
126
  ;
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,
@@ -152,7 +135,7 @@ static const rb_data_type_t IO_Event_Selector_URing_Type = {
152
135
  .dsize = IO_Event_Selector_URing_Type_size,
153
136
  },
154
137
  .data = NULL,
155
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
138
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
156
139
  };
157
140
 
158
141
  inline static
@@ -228,7 +211,7 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
228
211
  struct IO_Event_Selector_URing *selector = NULL;
229
212
  VALUE instance = TypedData_Make_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
230
213
 
231
- IO_Event_Selector_initialize(&selector->backend, Qnil);
214
+ IO_Event_Selector_initialize(&selector->backend, self, Qnil);
232
215
  selector->ring.ring_fd = -1;
233
216
 
234
217
  selector->pending = 0;
@@ -238,7 +221,10 @@ 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
- IO_Event_Array_allocate(&selector->completions, 1024, 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));
225
+ if (result < 0) {
226
+ rb_sys_fail("IO_Event_Selector_URing_allocate:IO_Event_Array_initialize");
227
+ }
242
228
 
243
229
  return instance;
244
230
  }
@@ -249,7 +235,7 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
249
235
  struct IO_Event_Selector_URing *selector = NULL;
250
236
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
251
237
 
252
- IO_Event_Selector_initialize(&selector->backend, loop);
238
+ IO_Event_Selector_initialize(&selector->backend, self, loop);
253
239
  int result = io_uring_queue_init(URING_ENTRIES, &selector->ring, 0);
254
240
 
255
241
  if (result < 0) {
@@ -291,7 +277,7 @@ VALUE IO_Event_Selector_URing_transfer(VALUE self)
291
277
  struct IO_Event_Selector_URing *selector = NULL;
292
278
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
293
279
 
294
- return IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
280
+ return IO_Event_Selector_loop_yield(&selector->backend);
295
281
  }
296
282
 
297
283
  VALUE IO_Event_Selector_URing_resume(int argc, VALUE *argv, VALUE self)
@@ -315,7 +301,7 @@ VALUE IO_Event_Selector_URing_push(VALUE self, VALUE fiber)
315
301
  struct IO_Event_Selector_URing *selector = NULL;
316
302
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
317
303
 
318
- IO_Event_Selector_queue_push(&selector->backend, fiber);
304
+ IO_Event_Selector_ready_push(&selector->backend, fiber);
319
305
 
320
306
  return Qnil;
321
307
  }
@@ -447,7 +433,7 @@ static
447
433
  VALUE process_wait_transfer(VALUE _arguments) {
448
434
  struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
449
435
 
450
- IO_Event_Selector_fiber_transfer(arguments->selector->backend.loop, 0, NULL);
436
+ IO_Event_Selector_loop_yield(&arguments->selector->backend);
451
437
 
452
438
  if (arguments->waiting->result) {
453
439
  return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
@@ -484,6 +470,8 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
484
470
  .fiber = fiber,
485
471
  };
486
472
 
473
+ RB_OBJ_WRITTEN(self, Qundef, fiber);
474
+
487
475
  struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
488
476
 
489
477
  struct process_wait_arguments process_wait_arguments = {
@@ -560,7 +548,7 @@ VALUE io_wait_transfer(VALUE _arguments) {
560
548
  struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
561
549
  struct IO_Event_Selector_URing *selector = arguments->selector;
562
550
 
563
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
551
+ IO_Event_Selector_loop_yield(&selector->backend);
564
552
 
565
553
  if (DEBUG) fprintf(stderr, "io_wait_transfer:waiting=%p, result=%d\n", (void*)arguments->waiting, arguments->waiting->result);
566
554
 
@@ -587,6 +575,8 @@ VALUE IO_Event_Selector_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
587
575
  .fiber = fiber,
588
576
  };
589
577
 
578
+ RB_OBJ_WRITTEN(self, Qundef, fiber);
579
+
590
580
  struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
591
581
 
592
582
  struct io_uring_sqe *sqe = io_get_sqe(selector);
@@ -630,6 +620,7 @@ struct io_read_arguments {
630
620
  struct IO_Event_Selector_URing *selector;
631
621
  struct IO_Event_Selector_URing_Waiting *waiting;
632
622
  int descriptor;
623
+ off_t offset;
633
624
  char *buffer;
634
625
  size_t length;
635
626
  };
@@ -643,11 +634,11 @@ io_read_submit(VALUE _arguments)
643
634
  if (DEBUG) fprintf(stderr, "io_read_submit:io_uring_prep_read(waiting=%p, completion=%p, descriptor=%d, buffer=%p, length=%ld)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion, arguments->descriptor, arguments->buffer, arguments->length);
644
635
 
645
636
  struct io_uring_sqe *sqe = io_get_sqe(selector);
646
- io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, io_seekable(arguments->descriptor));
637
+ io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
647
638
  io_uring_sqe_set_data(sqe, arguments->waiting->completion);
648
639
  io_uring_submit_now(selector);
649
640
 
650
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
641
+ IO_Event_Selector_loop_yield(&selector->backend);
651
642
 
652
643
  return RB_INT2NUM(arguments->waiting->result);
653
644
  }
@@ -673,18 +664,21 @@ io_read_ensure(VALUE _arguments)
673
664
  }
674
665
 
675
666
  static int
676
- io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
667
+ io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
677
668
  {
678
669
  struct IO_Event_Selector_URing_Waiting waiting = {
679
670
  .fiber = fiber,
680
671
  };
681
672
 
673
+ RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
674
+
682
675
  IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
683
676
 
684
677
  struct io_read_arguments io_read_arguments = {
685
678
  .selector = selector,
686
679
  .waiting = &waiting,
687
680
  .descriptor = descriptor,
681
+ .offset = offset,
688
682
  .buffer = buffer,
689
683
  .length = length
690
684
  };
@@ -707,10 +701,11 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
707
701
  size_t length = NUM2SIZET(_length);
708
702
  size_t offset = NUM2SIZET(_offset);
709
703
  size_t total = 0;
704
+ off_t from = io_seekable(descriptor);
710
705
 
711
706
  size_t maximum_size = size - offset;
712
707
  while (maximum_size) {
713
- int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size);
708
+ int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
714
709
 
715
710
  if (result > 0) {
716
711
  total += result;
@@ -744,12 +739,52 @@ static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, V
744
739
  return IO_Event_Selector_URing_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
745
740
  }
746
741
 
742
+ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
743
+ struct IO_Event_Selector_URing *selector = NULL;
744
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
745
+
746
+ int descriptor = IO_Event_Selector_io_descriptor(io);
747
+
748
+ void *base;
749
+ size_t size;
750
+ rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
751
+
752
+ size_t length = NUM2SIZET(_length);
753
+ size_t offset = NUM2SIZET(_offset);
754
+ size_t total = 0;
755
+ off_t from = NUM2OFFT(_from);
756
+
757
+ size_t maximum_size = size - offset;
758
+ while (maximum_size) {
759
+ int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
760
+
761
+ if (result > 0) {
762
+ total += result;
763
+ offset += result;
764
+ from += result;
765
+ if ((size_t)result >= length) break;
766
+ length -= result;
767
+ } else if (result == 0) {
768
+ break;
769
+ } else if (length > 0 && IO_Event_try_again(-result)) {
770
+ IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_READABLE));
771
+ } else {
772
+ return rb_fiber_scheduler_io_result(-1, -result);
773
+ }
774
+
775
+ maximum_size = size - offset;
776
+ }
777
+
778
+ return rb_fiber_scheduler_io_result(total, 0);
779
+ }
780
+
747
781
  #pragma mark - IO#write
748
782
 
749
783
  struct io_write_arguments {
750
784
  struct IO_Event_Selector_URing *selector;
751
785
  struct IO_Event_Selector_URing_Waiting *waiting;
752
786
  int descriptor;
787
+ off_t offset;
753
788
  char *buffer;
754
789
  size_t length;
755
790
  };
@@ -763,11 +798,11 @@ io_write_submit(VALUE _argument)
763
798
  if (DEBUG) fprintf(stderr, "io_write_submit:io_uring_prep_write(waiting=%p, completion=%p, descriptor=%d, buffer=%p, length=%ld)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion, arguments->descriptor, arguments->buffer, arguments->length);
764
799
 
765
800
  struct io_uring_sqe *sqe = io_get_sqe(selector);
766
- io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length, io_seekable(arguments->descriptor));
801
+ io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
767
802
  io_uring_sqe_set_data(sqe, arguments->waiting->completion);
768
803
  io_uring_submit_pending(selector);
769
804
 
770
- IO_Event_Selector_fiber_transfer(selector->backend.loop, 0, NULL);
805
+ IO_Event_Selector_loop_yield(&selector->backend);
771
806
 
772
807
  return RB_INT2NUM(arguments->waiting->result);
773
808
  }
@@ -793,18 +828,21 @@ io_write_ensure(VALUE _argument)
793
828
  }
794
829
 
795
830
  static int
796
- io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
831
+ io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
797
832
  {
798
833
  struct IO_Event_Selector_URing_Waiting waiting = {
799
834
  .fiber = fiber,
800
835
  };
801
836
 
837
+ RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
838
+
802
839
  IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
803
840
 
804
841
  struct io_write_arguments arguments = {
805
842
  .selector = selector,
806
843
  .waiting = &waiting,
807
844
  .descriptor = descriptor,
845
+ .offset = offset,
808
846
  .buffer = buffer,
809
847
  .length = length,
810
848
  };
@@ -827,6 +865,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
827
865
  size_t length = NUM2SIZET(_length);
828
866
  size_t offset = NUM2SIZET(_offset);
829
867
  size_t total = 0;
868
+ off_t from = io_seekable(descriptor);
830
869
 
831
870
  if (length > size) {
832
871
  rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
@@ -834,7 +873,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
834
873
 
835
874
  size_t maximum_size = size - offset;
836
875
  while (maximum_size) {
837
- int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size);
876
+ int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
838
877
 
839
878
  if (result > 0) {
840
879
  total += result;
@@ -868,6 +907,49 @@ static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv,
868
907
  return IO_Event_Selector_URing_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
869
908
  }
870
909
 
910
+ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
911
+ struct IO_Event_Selector_URing *selector = NULL;
912
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
913
+
914
+ int descriptor = IO_Event_Selector_io_descriptor(io);
915
+
916
+ const void *base;
917
+ size_t size;
918
+ rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
919
+
920
+ size_t length = NUM2SIZET(_length);
921
+ size_t offset = NUM2SIZET(_offset);
922
+ size_t total = 0;
923
+ off_t from = NUM2OFFT(_from);
924
+
925
+ if (length > size) {
926
+ rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
927
+ }
928
+
929
+ size_t maximum_size = size - offset;
930
+ while (maximum_size) {
931
+ int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
932
+
933
+ if (result > 0) {
934
+ total += result;
935
+ offset += result;
936
+ from += result;
937
+ if ((size_t)result >= length) break;
938
+ length -= result;
939
+ } else if (result == 0) {
940
+ break;
941
+ } else if (length > 0 && IO_Event_try_again(-result)) {
942
+ IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_WRITABLE));
943
+ } else {
944
+ return rb_fiber_scheduler_io_result(-1, -result);
945
+ }
946
+
947
+ maximum_size = size - offset;
948
+ }
949
+
950
+ return rb_fiber_scheduler_io_result(total, 0);
951
+ }
952
+
871
953
  #endif
872
954
 
873
955
  #pragma mark - IO#close
@@ -1004,7 +1086,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
1004
1086
  if (waiting && waiting->fiber) {
1005
1087
  assert(waiting->result != -ECANCELED);
1006
1088
 
1007
- IO_Event_Selector_fiber_transfer(waiting->fiber, 0, NULL);
1089
+ IO_Event_Selector_loop_resume(&selector->backend, waiting->fiber, 0, NULL);
1008
1090
  }
1009
1091
  }
1010
1092
 
@@ -1023,7 +1105,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
1023
1105
  // Flush any pending events:
1024
1106
  io_uring_submit_flush(selector);
1025
1107
 
1026
- int ready = IO_Event_Selector_queue_flush(&selector->backend);
1108
+ int ready = IO_Event_Selector_ready_flush(&selector->backend);
1027
1109
 
1028
1110
  int result = select_process_completions(selector);
1029
1111
 
@@ -1043,14 +1125,14 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
1043
1125
 
1044
1126
  if (!selector->backend.ready && !timeout_nonblocking(arguments.timeout)) {
1045
1127
  struct timespec start_time;
1046
- IO_Event_Selector_current_time(&start_time);
1128
+ IO_Event_Time_current(&start_time);
1047
1129
 
1048
1130
  // This is a blocking operation, we wait for events:
1049
1131
  result = select_internal_without_gvl(&arguments);
1050
1132
 
1051
1133
  struct timespec end_time;
1052
- IO_Event_Selector_current_time(&end_time);
1053
- 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);
1054
1136
 
1055
1137
  // After waiting/flushing the SQ, check if there are any completions:
1056
1138
  if (result > 0) {
@@ -1094,8 +1176,7 @@ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
1094
1176
  #pragma mark - Native Methods
1095
1177
 
1096
1178
  void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
1097
- IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
1098
- rb_gc_register_mark_object(IO_Event_Selector_URing);
1179
+ VALUE IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
1099
1180
 
1100
1181
  rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
1101
1182
  rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
@@ -1120,6 +1201,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
1120
1201
  #ifdef HAVE_RUBY_IO_BUFFER_H
1121
1202
  rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read_compatible, -1);
1122
1203
  rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write_compatible, -1);
1204
+ rb_define_method(IO_Event_Selector_URing, "io_pread", IO_Event_Selector_URing_io_pread, 6);
1205
+ rb_define_method(IO_Event_Selector_URing, "io_pwrite", IO_Event_Selector_URing_io_pwrite, 6);
1123
1206
  #endif
1124
1207
 
1125
1208
  rb_define_method(IO_Event_Selector_URing, "io_close", IO_Event_Selector_URing_io_close, 1);
@@ -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)
@@ -3,22 +3,33 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
- require_relative '../support'
6
+ require_relative "../support"
7
7
 
8
8
  module IO::Event
9
+ # @namespace
9
10
  module Debug
10
11
  # Enforces the selector interface and delegates operations to a wrapped selector instance.
12
+ #
13
+ # You can enable this in the default selector by setting the `IO_EVENT_DEBUG_SELECTOR` environment variable. In addition, you can log all selector operations to a file by setting the `IO_EVENT_DEBUG_SELECTOR_LOG` environment variable. This is useful for debugging and understanding the behavior of the event loop.
11
14
  class Selector
15
+ # Wrap the given selector with debugging.
16
+ #
17
+ # @parameter selector [Selector] The selector to wrap.
18
+ # @parameter env [Hash] The environment to read configuration from.
12
19
  def self.wrap(selector, env = ENV)
13
20
  log = nil
14
21
 
15
- if log_path = env['IO_EVENT_DEBUG_SELECTOR_LOG']
16
- log = File.open(log_path, 'w')
22
+ if log_path = env["IO_EVENT_DEBUG_SELECTOR_LOG"]
23
+ log = File.open(log_path, "w")
17
24
  end
18
25
 
19
26
  return self.new(selector, log: log)
20
27
  end
21
28
 
29
+ # Initialize the debug selector with the given selector and optional log.
30
+ #
31
+ # @parameter selector [Selector] The selector to wrap.
32
+ # @parameter log [IO] The log to write debug messages to.
22
33
  def initialize(selector, log: nil)
23
34
  @selector = selector
24
35
 
@@ -33,14 +44,23 @@ module IO::Event
33
44
  @log = log
34
45
  end
35
46
 
47
+ # The idle duration of the underlying selector.
48
+ #
49
+ # @returns [Numeric] The idle duration.
36
50
  def idle_duration
37
51
  @selector.idle_duration
38
52
  end
39
53
 
54
+ # The current time.
55
+ #
56
+ # @returns [Numeric] The current time.
40
57
  def now
41
58
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
42
59
  end
43
60
 
61
+ # Log the given message.
62
+ #
63
+ # @asynchronous Will block the calling fiber and the entire event loop.
44
64
  def log(message)
45
65
  return unless @log
46
66
 
@@ -49,10 +69,12 @@ module IO::Event
49
69
  end
50
70
  end
51
71
 
72
+ # Wakeup the the selector.
52
73
  def wakeup
53
74
  @selector.wakeup
54
75
  end
55
76
 
77
+ # Close the selector.
56
78
  def close
57
79
  log("Closing selector")
58
80
 
@@ -64,60 +86,78 @@ module IO::Event
64
86
  @selector = nil
65
87
  end
66
88
 
67
- # Transfer from the calling fiber to the event loop.
89
+ # Transfer from the calling fiber to the selector.
68
90
  def transfer
69
91
  log("Transfering to event loop")
70
92
  @selector.transfer
71
93
  end
72
94
 
95
+ # Resume the given fiber with the given arguments.
73
96
  def resume(*arguments)
74
97
  log("Resuming fiber with #{arguments.inspect}")
75
98
  @selector.resume(*arguments)
76
99
  end
77
100
 
101
+ # Yield to the selector.
78
102
  def yield
79
103
  log("Yielding to event loop")
80
104
  @selector.yield
81
105
  end
82
106
 
107
+ # Push the given fiber to the selector ready list, such that it will be resumed on the next call to {select}.
108
+ #
109
+ # @parameter fiber [Fiber] The fiber that is ready.
83
110
  def push(fiber)
84
111
  log("Pushing fiber #{fiber.inspect} to ready list")
85
112
  @selector.push(fiber)
86
113
  end
87
114
 
115
+ # Raise the given exception on the given fiber.
116
+ #
117
+ # @parameter fiber [Fiber] The fiber to raise the exception on.
118
+ # @parameter arguments [Array] The arguments to use when raising the exception.
88
119
  def raise(fiber, *arguments)
89
120
  log("Raising exception on fiber #{fiber.inspect} with #{arguments.inspect}")
90
121
  @selector.raise(fiber, *arguments)
91
122
  end
92
123
 
124
+ # Check if the selector is ready.
125
+ #
126
+ # @returns [Boolean] Whether the selector is ready.
93
127
  def ready?
94
128
  @selector.ready?
95
129
  end
96
130
 
131
+ # Wait for the given process, forwarded to the underlying selector.
97
132
  def process_wait(*arguments)
98
133
  log("Waiting for process with #{arguments.inspect}")
99
134
  @selector.process_wait(*arguments)
100
135
  end
101
136
 
137
+ # Wait for the given IO, forwarded to the underlying selector.
102
138
  def io_wait(fiber, io, events)
103
139
  log("Waiting for IO #{io.inspect} for events #{events.inspect}")
104
140
  @selector.io_wait(fiber, io, events)
105
141
  end
106
142
 
143
+ # Read from the given IO, forwarded to the underlying selector.
107
144
  def io_read(fiber, io, buffer, length, offset = 0)
108
145
  log("Reading from IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
109
146
  @selector.io_read(fiber, io, buffer, length, offset)
110
147
  end
111
148
 
149
+ # Write to the given IO, forwarded to the underlying selector.
112
150
  def io_write(fiber, io, buffer, length, offset = 0)
113
151
  log("Writing to IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
114
152
  @selector.io_write(fiber, io, buffer, length, offset)
115
153
  end
116
154
 
155
+ # Forward the given method to the underlying selector.
117
156
  def respond_to?(name, include_private = false)
118
157
  @selector.respond_to?(name, include_private)
119
158
  end
120
159
 
160
+ # Select for the given duration, forwarded to the underlying selector.
121
161
  def select(duration = nil)
122
162
  log("Selecting for #{duration.inspect}")
123
163
  unless Fiber.current == @selector.loop
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2023, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  module IO::Event
7
7
  # A thread safe synchronisation primative.
@@ -27,7 +27,7 @@ module IO::Event
27
27
 
28
28
  # Send a sigle byte interrupt.
29
29
  def signal
30
- @output.write('.')
30
+ @output.write(".")
31
31
  @output.flush
32
32
  rescue IOError
33
33
  # Ignore.
@@ -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