io-event 1.6.5 → 1.7.5

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.
@@ -35,10 +35,9 @@
35
35
  enum {
36
36
  DEBUG = 0,
37
37
  DEBUG_COMPLETION = 0,
38
+ DEBUG_IO_READ = 1,
38
39
  };
39
40
 
40
- static VALUE IO_Event_Selector_URing = Qnil;
41
-
42
41
  enum {URING_ENTRIES = 64};
43
42
 
44
43
  #pragma mark - Data Type
@@ -140,6 +139,7 @@ size_t IO_Event_Selector_URing_Type_size(const void *_selector)
140
139
 
141
140
  return sizeof(struct IO_Event_Selector_URing)
142
141
  + IO_Event_Array_memory_size(&selector->completions)
142
+ + IO_Event_List_memory_size(&selector->free_list)
143
143
  ;
144
144
  }
145
145
 
@@ -152,7 +152,7 @@ static const rb_data_type_t IO_Event_Selector_URing_Type = {
152
152
  .dsize = IO_Event_Selector_URing_Type_size,
153
153
  },
154
154
  .data = NULL,
155
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
155
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
156
156
  };
157
157
 
158
158
  inline static
@@ -228,7 +228,7 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
228
228
  struct IO_Event_Selector_URing *selector = NULL;
229
229
  VALUE instance = TypedData_Make_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
230
230
 
231
- IO_Event_Selector_initialize(&selector->backend, Qnil);
231
+ IO_Event_Selector_initialize(&selector->backend, self, Qnil);
232
232
  selector->ring.ring_fd = -1;
233
233
 
234
234
  selector->pending = 0;
@@ -238,7 +238,10 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
238
238
 
239
239
  selector->completions.element_initialize = IO_Event_Selector_URing_Completion_initialize;
240
240
  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));
241
+ int result = IO_Event_Array_allocate(&selector->completions, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_URing_Completion));
242
+ if (result < 0) {
243
+ rb_sys_fail("IO_Event_Selector_URing_allocate:IO_Event_Array_allocate");
244
+ }
242
245
 
243
246
  return instance;
244
247
  }
@@ -249,7 +252,7 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
249
252
  struct IO_Event_Selector_URing *selector = NULL;
250
253
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
251
254
 
252
- IO_Event_Selector_initialize(&selector->backend, loop);
255
+ IO_Event_Selector_initialize(&selector->backend, self, loop);
253
256
  int result = io_uring_queue_init(URING_ENTRIES, &selector->ring, 0);
254
257
 
255
258
  if (result < 0) {
@@ -484,6 +487,8 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
484
487
  .fiber = fiber,
485
488
  };
486
489
 
490
+ RB_OBJ_WRITTEN(self, Qundef, fiber);
491
+
487
492
  struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
488
493
 
489
494
  struct process_wait_arguments process_wait_arguments = {
@@ -587,6 +592,8 @@ VALUE IO_Event_Selector_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
587
592
  .fiber = fiber,
588
593
  };
589
594
 
595
+ RB_OBJ_WRITTEN(self, Qundef, fiber);
596
+
590
597
  struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
591
598
 
592
599
  struct io_uring_sqe *sqe = io_get_sqe(selector);
@@ -630,6 +637,7 @@ struct io_read_arguments {
630
637
  struct IO_Event_Selector_URing *selector;
631
638
  struct IO_Event_Selector_URing_Waiting *waiting;
632
639
  int descriptor;
640
+ off_t offset;
633
641
  char *buffer;
634
642
  size_t length;
635
643
  };
@@ -643,7 +651,7 @@ io_read_submit(VALUE _arguments)
643
651
  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
652
 
645
653
  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));
654
+ io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
647
655
  io_uring_sqe_set_data(sqe, arguments->waiting->completion);
648
656
  io_uring_submit_now(selector);
649
657
 
@@ -673,18 +681,21 @@ io_read_ensure(VALUE _arguments)
673
681
  }
674
682
 
675
683
  static int
676
- io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
684
+ io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
677
685
  {
678
686
  struct IO_Event_Selector_URing_Waiting waiting = {
679
687
  .fiber = fiber,
680
688
  };
681
689
 
690
+ RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
691
+
682
692
  IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
683
693
 
684
694
  struct io_read_arguments io_read_arguments = {
685
695
  .selector = selector,
686
696
  .waiting = &waiting,
687
697
  .descriptor = descriptor,
698
+ .offset = offset,
688
699
  .buffer = buffer,
689
700
  .length = length
690
701
  };
@@ -707,10 +718,11 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
707
718
  size_t length = NUM2SIZET(_length);
708
719
  size_t offset = NUM2SIZET(_offset);
709
720
  size_t total = 0;
721
+ off_t from = io_seekable(descriptor);
710
722
 
711
723
  size_t maximum_size = size - offset;
712
724
  while (maximum_size) {
713
- int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size);
725
+ int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
714
726
 
715
727
  if (result > 0) {
716
728
  total += result;
@@ -744,12 +756,52 @@ static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, V
744
756
  return IO_Event_Selector_URing_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
745
757
  }
746
758
 
759
+ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
760
+ struct IO_Event_Selector_URing *selector = NULL;
761
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
762
+
763
+ int descriptor = IO_Event_Selector_io_descriptor(io);
764
+
765
+ void *base;
766
+ size_t size;
767
+ rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
768
+
769
+ size_t length = NUM2SIZET(_length);
770
+ size_t offset = NUM2SIZET(_offset);
771
+ size_t total = 0;
772
+ off_t from = NUM2OFFT(_from);
773
+
774
+ size_t maximum_size = size - offset;
775
+ while (maximum_size) {
776
+ int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
777
+
778
+ if (result > 0) {
779
+ total += result;
780
+ offset += result;
781
+ from += result;
782
+ if ((size_t)result >= length) break;
783
+ length -= result;
784
+ } else if (result == 0) {
785
+ break;
786
+ } else if (length > 0 && IO_Event_try_again(-result)) {
787
+ IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_READABLE));
788
+ } else {
789
+ return rb_fiber_scheduler_io_result(-1, -result);
790
+ }
791
+
792
+ maximum_size = size - offset;
793
+ }
794
+
795
+ return rb_fiber_scheduler_io_result(total, 0);
796
+ }
797
+
747
798
  #pragma mark - IO#write
748
799
 
749
800
  struct io_write_arguments {
750
801
  struct IO_Event_Selector_URing *selector;
751
802
  struct IO_Event_Selector_URing_Waiting *waiting;
752
803
  int descriptor;
804
+ off_t offset;
753
805
  char *buffer;
754
806
  size_t length;
755
807
  };
@@ -763,7 +815,7 @@ io_write_submit(VALUE _argument)
763
815
  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
816
 
765
817
  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));
818
+ io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
767
819
  io_uring_sqe_set_data(sqe, arguments->waiting->completion);
768
820
  io_uring_submit_pending(selector);
769
821
 
@@ -793,18 +845,21 @@ io_write_ensure(VALUE _argument)
793
845
  }
794
846
 
795
847
  static int
796
- io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
848
+ io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
797
849
  {
798
850
  struct IO_Event_Selector_URing_Waiting waiting = {
799
851
  .fiber = fiber,
800
852
  };
801
853
 
854
+ RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
855
+
802
856
  IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
803
857
 
804
858
  struct io_write_arguments arguments = {
805
859
  .selector = selector,
806
860
  .waiting = &waiting,
807
861
  .descriptor = descriptor,
862
+ .offset = offset,
808
863
  .buffer = buffer,
809
864
  .length = length,
810
865
  };
@@ -827,6 +882,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
827
882
  size_t length = NUM2SIZET(_length);
828
883
  size_t offset = NUM2SIZET(_offset);
829
884
  size_t total = 0;
885
+ off_t from = io_seekable(descriptor);
830
886
 
831
887
  if (length > size) {
832
888
  rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
@@ -834,7 +890,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
834
890
 
835
891
  size_t maximum_size = size - offset;
836
892
  while (maximum_size) {
837
- int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size);
893
+ int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
838
894
 
839
895
  if (result > 0) {
840
896
  total += result;
@@ -868,6 +924,49 @@ static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv,
868
924
  return IO_Event_Selector_URing_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
869
925
  }
870
926
 
927
+ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
928
+ struct IO_Event_Selector_URing *selector = NULL;
929
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
930
+
931
+ int descriptor = IO_Event_Selector_io_descriptor(io);
932
+
933
+ const void *base;
934
+ size_t size;
935
+ rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
936
+
937
+ size_t length = NUM2SIZET(_length);
938
+ size_t offset = NUM2SIZET(_offset);
939
+ size_t total = 0;
940
+ off_t from = NUM2OFFT(_from);
941
+
942
+ if (length > size) {
943
+ rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
944
+ }
945
+
946
+ size_t maximum_size = size - offset;
947
+ while (maximum_size) {
948
+ int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
949
+
950
+ if (result > 0) {
951
+ total += result;
952
+ offset += result;
953
+ from += result;
954
+ if ((size_t)result >= length) break;
955
+ length -= result;
956
+ } else if (result == 0) {
957
+ break;
958
+ } else if (length > 0 && IO_Event_try_again(-result)) {
959
+ IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_WRITABLE));
960
+ } else {
961
+ return rb_fiber_scheduler_io_result(-1, -result);
962
+ }
963
+
964
+ maximum_size = size - offset;
965
+ }
966
+
967
+ return rb_fiber_scheduler_io_result(total, 0);
968
+ }
969
+
871
970
  #endif
872
971
 
873
972
  #pragma mark - IO#close
@@ -1094,8 +1193,7 @@ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
1094
1193
  #pragma mark - Native Methods
1095
1194
 
1096
1195
  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);
1196
+ VALUE IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
1099
1197
 
1100
1198
  rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
1101
1199
  rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
@@ -1120,6 +1218,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
1120
1218
  #ifdef HAVE_RUBY_IO_BUFFER_H
1121
1219
  rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read_compatible, -1);
1122
1220
  rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write_compatible, -1);
1221
+ rb_define_method(IO_Event_Selector_URing, "io_pread", IO_Event_Selector_URing_io_pread, 6);
1222
+ rb_define_method(IO_Event_Selector_URing, "io_pwrite", IO_Event_Selector_URing_io_pwrite, 6);
1123
1223
  #endif
1124
1224
 
1125
1225
  rb_define_method(IO_Event_Selector_URing, "io_close", IO_Event_Selector_URing_io_close, 1);
@@ -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.
@@ -10,6 +10,7 @@ class IO
10
10
  # of its contents to determine priority.
11
11
  # See <https://en.wikipedia.org/wiki/Binary_heap> for explanations of the main methods.
12
12
  class PriorityHeap
13
+ # Initializes the heap.
13
14
  def initialize
14
15
  # The heap is represented with an array containing a binary tree. See
15
16
  # https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation for how this array
@@ -17,18 +18,19 @@ class IO
17
18
  @contents = []
18
19
  end
19
20
 
20
- # Returns the earliest timer or nil if the heap is empty.
21
+ # @returns [Object | Nil] the smallest element in the heap without removing it, or nil if the heap is empty.
21
22
  def peek
22
23
  @contents[0]
23
24
  end
24
25
 
25
- # Returns the number of elements in the heap
26
+ # @returns [Integer] the number of elements in the heap.
26
27
  def size
27
28
  @contents.size
28
29
  end
29
30
 
30
- # Returns the earliest timer if the heap is non-empty and removes it from the heap.
31
- # Returns nil if the heap is empty. (and doesn't change the heap in that case)
31
+ # Removes and returns the smallest element in the heap, or nil if the heap is empty.
32
+ #
33
+ # @returns [Object | Nil] The smallest element in the heap, or nil if the heap is empty.
32
34
  def pop
33
35
  # If the heap is empty:
34
36
  if @contents.empty?
@@ -57,7 +59,9 @@ class IO
57
59
  return value
58
60
  end
59
61
 
60
- # Inserts a new timer into the heap, then rearranges elements until the heap invariant is true again.
62
+ # Add a new element to the heap, then rearrange elements until the heap invariant is true again.
63
+ #
64
+ # @parameter element [Object] The element to add to the heap.
61
65
  def push(element)
62
66
  # Insert the item at the end of the heap:
63
67
  @contents.push(element)
@@ -75,10 +79,9 @@ class IO
75
79
  @contents = []
76
80
  end
77
81
 
78
- # Validate the heap invariant. Every element except the root must not be smaller than
79
- # its parent element. Note that it MAY be equal.
82
+ # Validate the heap invariant. Every element except the root must not be smaller than its parent element. Note that it MAY be equal.
80
83
  def valid?
81
- # notice we skip index 0 on purpose, because it has no parent
84
+ # Notice we skip index 0 on purpose, because it has no parent
82
85
  (1..(@contents.size - 1)).all? { |e| @contents[e] >= @contents[(e - 1) / 2] }
83
86
  end
84
87
 
@@ -93,10 +96,7 @@ class IO
93
96
  parent_index = (index - 1) / 2 # watch out, integer division!
94
97
 
95
98
  while index > 0 && @contents[index] < @contents[parent_index]
96
- # if the node has a smaller value than its parent, swap these nodes
97
- # to uphold the minheap invariant and update the index of the 'current'
98
- # node. If the node is already at index 0, we can also stop because that
99
- # is the root of the heap.
99
+ # If the node has a smaller value than its parent, swap these nodes to uphold the minheap invariant and update the index of the 'current' node. If the node is already at index 0, we can also stop because that is the root of the heap.
100
100
  # swap(index, parent_index)
101
101
  @contents[index], @contents[parent_index] = @contents[parent_index], @contents[index]
102
102
 
@@ -114,8 +114,7 @@ class IO
114
114
  left_value = @contents[left_index]
115
115
 
116
116
  if left_value.nil?
117
- # This node has no children so it can't bubble down any further.
118
- # We're done here!
117
+ # This node has no children so it can't bubble down any further. We're done here!
119
118
  return
120
119
  end
121
120
 
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2023, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
- require 'io/nonblock'
6
+ require "io/nonblock"
7
7
 
8
8
  module IO::Event
9
9
  module Selector
10
+ # Execute the given block in non-blocking mode.
11
+ #
12
+ # @parameter io [IO] The IO object to operate on.
13
+ # @yields {...} The block to execute.
10
14
  def self.nonblock(io, &block)
11
15
  io.nonblock(&block)
12
16
  rescue Errno::EBADF
@@ -4,12 +4,14 @@
4
4
  # Copyright, 2021-2024, by Samuel Williams.
5
5
  # Copyright, 2023, by Math Ieu.
6
6
 
7
- require_relative '../interrupt'
8
- require_relative '../support'
7
+ require_relative "../interrupt"
8
+ require_relative "../support"
9
9
 
10
10
  module IO::Event
11
11
  module Selector
12
+ # A pure-Ruby implementation of the event selector.
12
13
  class Select
14
+ # Initialize the selector with the given event loop fiber.
13
15
  def initialize(loop)
14
16
  @loop = loop
15
17
 
@@ -23,12 +25,13 @@ module IO::Event
23
25
  @idle_duration = 0.0
24
26
  end
25
27
 
28
+ # @attribute [Fiber] The event loop fiber.
26
29
  attr :loop
27
30
 
28
- # This is the amount of time the event loop was idle during the last select call.
31
+ # @attribute [Float] This is the amount of time the event loop was idle during the last select call.
29
32
  attr :idle_duration
30
33
 
31
- # If the event loop is currently sleeping, wake it up.
34
+ # Wake up the event loop if it is currently sleeping.
32
35
  def wakeup
33
36
  if @blocked
34
37
  @interrupt.signal
@@ -39,6 +42,7 @@ module IO::Event
39
42
  return false
40
43
  end
41
44
 
45
+ # Close the selector and release any resources.
42
46
  def close
43
47
  @interrupt.close
44
48
 
@@ -100,6 +104,7 @@ module IO::Event
100
104
  optional.nullify
101
105
  end
102
106
 
107
+ # @returns [Boolean] Whether the ready list is not empty, i.e. there are fibers ready to be resumed.
103
108
  def ready?
104
109
  !@ready.empty?
105
110
  end
@@ -144,6 +149,11 @@ module IO::Event
144
149
  end
145
150
  end
146
151
 
152
+ # Wait for the given IO to become readable or writable.
153
+ #
154
+ # @parameter fiber [Fiber] The fiber that is waiting.
155
+ # @parameter io [IO] The IO object to wait on.
156
+ # @parameter events [Integer] The events to wait for.
147
157
  def io_wait(fiber, io, events)
148
158
  waiter = @waiting[io] = Waiter.new(fiber, events, @waiting[io])
149
159
 
@@ -152,6 +162,11 @@ module IO::Event
152
162
  waiter&.invalidate
153
163
  end
154
164
 
165
+ # Wait for multiple IO objects to become readable or writable.
166
+ #
167
+ # @parameter readable [Array(IO)] The list of IO objects to wait for readability.
168
+ # @parameter writable [Array(IO)] The list of IO objects to wait for writability.
169
+ # @parameter priority [Array(IO)] The list of IO objects to wait for priority events.
155
170
  def io_select(readable, writable, priority, timeout)
156
171
  Thread.new do
157
172
  IO.select(readable, writable, priority, timeout)
@@ -161,13 +176,16 @@ module IO::Event
161
176
  EAGAIN = -Errno::EAGAIN::Errno
162
177
  EWOULDBLOCK = -Errno::EWOULDBLOCK::Errno
163
178
 
164
- def again?(errno)
179
+ # Whether the given error code indicates that the operation should be retried.
180
+ protected def again?(errno)
165
181
  errno == EAGAIN or errno == EWOULDBLOCK
166
182
  end
167
183
 
168
184
  if Support.fiber_scheduler_v3?
169
185
  # Ruby 3.3+, full IO::Buffer support.
170
186
 
187
+ # Read from the given IO to the buffer.
188
+ #
171
189
  # @parameter length [Integer] The minimum number of bytes to read.
172
190
  # @parameter offset [Integer] The offset into the buffer to read to.
173
191
  def io_read(fiber, io, buffer, length, offset = 0)
@@ -196,6 +214,8 @@ module IO::Event
196
214
  return total
197
215
  end
198
216
 
217
+ # Write to the given IO from the buffer.
218
+ #
199
219
  # @parameter length [Integer] The minimum number of bytes to write.
200
220
  # @parameter offset [Integer] The offset into the buffer to write from.
201
221
  def io_write(fiber, io, buffer, length, offset = 0)