io-event 1.16.2 → 1.21.1

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.
@@ -9,6 +9,7 @@
9
9
  #include <sys/epoll.h>
10
10
  #include <time.h>
11
11
  #include <errno.h>
12
+ #include <unistd.h>
12
13
 
13
14
  #include "pidfd.c"
14
15
  #include "../interrupt.h"
@@ -38,11 +39,7 @@ struct IO_Event_Selector_EPoll
38
39
  {
39
40
  struct IO_Event_Selector backend;
40
41
  int descriptor;
41
-
42
- // Flag indicating whether the selector is currently blocked in a system call.
43
- // Set to 1 when blocked in epoll_wait() without GVL, 0 otherwise.
44
- // Used by wakeup() to determine if an interrupt signal is needed.
45
- int blocked;
42
+ pid_t owner;
46
43
 
47
44
  struct timespec idle_duration;
48
45
 
@@ -131,10 +128,12 @@ static
131
128
  void close_internal(struct IO_Event_Selector_EPoll *selector)
132
129
  {
133
130
  if (selector->descriptor >= 0) {
134
- close(selector->descriptor);
135
- selector->descriptor = -1;
131
+ if (selector->owner == getpid()) {
132
+ close(selector->descriptor);
133
+ IO_Event_Interrupt_close(&selector->interrupt);
134
+ }
136
135
 
137
- IO_Event_Interrupt_close(&selector->interrupt);
136
+ selector->descriptor = -1;
138
137
  }
139
138
  }
140
139
 
@@ -315,8 +314,7 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
315
314
 
316
315
  IO_Event_Selector_initialize(&selector->backend, self, Qnil);
317
316
  selector->descriptor = -1;
318
- selector->blocked = 0;
319
-
317
+ selector->owner = 0;
320
318
  selector->descriptors.element_initialize = IO_Event_Selector_EPoll_Descriptor_initialize;
321
319
  selector->descriptors.element_free = IO_Event_Selector_EPoll_Descriptor_free;
322
320
  IO_Event_Array_initialize(&selector->descriptors, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_EPoll_Descriptor));
@@ -350,6 +348,7 @@ VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
350
348
  rb_sys_fail("IO_Event_Selector_EPoll_initialize:epoll_create");
351
349
  } else {
352
350
  selector->descriptor = result;
351
+ selector->owner = getpid();
353
352
 
354
353
  rb_update_max_fd(selector->descriptor);
355
354
  }
@@ -385,6 +384,13 @@ VALUE IO_Event_Selector_EPoll_close(VALUE self) {
385
384
  return Qnil;
386
385
  }
387
386
 
387
+ VALUE IO_Event_Selector_EPoll_closed_p(VALUE self) {
388
+ struct IO_Event_Selector_EPoll *selector = NULL;
389
+ TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
390
+
391
+ return selector->descriptor < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
392
+ }
393
+
388
394
  VALUE IO_Event_Selector_EPoll_transfer(VALUE self)
389
395
  {
390
396
  struct IO_Event_Selector_EPoll *selector = NULL;
@@ -449,7 +455,7 @@ VALUE process_wait_transfer(VALUE _arguments) {
449
455
  IO_Event_Selector_loop_yield(&arguments->selector->backend);
450
456
 
451
457
  if (arguments->waiting->ready) {
452
- return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
458
+ return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
453
459
  } else {
454
460
  return Qfalse;
455
461
  }
@@ -475,6 +481,11 @@ VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid,
475
481
  pid_t pid = NUM2PIDT(_pid);
476
482
  int flags = NUM2INT(_flags);
477
483
 
484
+ // `pidfd_open` can only refer to a specific process, so waiting for any child or a process group (pid <= 0) is delegated to the threaded fallback:
485
+ if (pid <= 0) {
486
+ return IO_Event_Selector_process_wait(pid, flags);
487
+ }
488
+
478
489
  int descriptor = pidfd_open(pid, 0);
479
490
 
480
491
  if (descriptor == -1) {
@@ -484,7 +495,7 @@ VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid,
484
495
  rb_update_max_fd(descriptor);
485
496
 
486
497
  // `pidfd_open` (above) may be edge triggered, so we need to check if the process is already exited, and if so, return immediately, otherwise we will block indefinitely.
487
- VALUE status = IO_Event_Selector_process_status_wait(pid, flags);
498
+ VALUE status = IO_Event_Selector_process_status_reap(pid, flags);
488
499
  if (status != Qnil) {
489
500
  close(descriptor);
490
501
  return status;
@@ -582,44 +593,45 @@ VALUE IO_Event_Selector_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
582
593
  #ifdef HAVE_RUBY_IO_BUFFER_H
583
594
 
584
595
  struct io_read_arguments {
596
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
585
597
  VALUE self;
586
598
  VALUE fiber;
587
599
  VALUE io;
600
+ #endif
588
601
 
589
602
  int flags;
590
603
 
591
604
  int descriptor;
592
605
 
593
- VALUE buffer;
606
+ // The remaining writable buffer region after applying the requested offset.
607
+ void *base;
608
+ size_t size;
609
+
610
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
611
+ // The minimum number of bytes requested by the caller.
594
612
  size_t length;
595
- size_t offset;
613
+ #endif
596
614
  };
597
615
 
598
616
  static
599
617
  VALUE io_read_loop(VALUE _arguments) {
600
618
  struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
601
619
 
602
- void *base;
603
- size_t size;
604
- rb_io_buffer_get_bytes_for_writing(arguments->buffer, &base, &size);
605
-
620
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
621
+ ssize_t result = read(arguments->descriptor, arguments->base, arguments->size);
622
+ return rb_fiber_scheduler_io_result(result, errno);
623
+ #else
606
624
  size_t length = arguments->length;
607
- size_t offset = arguments->offset;
608
625
  size_t total = 0;
609
626
 
610
- // Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
611
- if (offset > size) {
612
- return rb_fiber_scheduler_io_result(-1, EINVAL);
613
- }
614
-
615
- size_t maximum_size = size - offset;
627
+ size_t maximum_size = arguments->size;
616
628
  while (maximum_size) {
617
- ssize_t result = read(arguments->descriptor, (char*)base+offset, maximum_size);
629
+ ssize_t result = read(arguments->descriptor, (char*)arguments->base+total, maximum_size);
618
630
 
619
631
  if (result > 0) {
620
632
  total += result;
621
- offset += result;
622
633
  if ((size_t)result >= length) break;
634
+ maximum_size -= result;
623
635
  length -= result;
624
636
  } else if (result == 0) {
625
637
  break;
@@ -628,11 +640,10 @@ VALUE io_read_loop(VALUE _arguments) {
628
640
  } else {
629
641
  return rb_fiber_scheduler_io_result(-1, errno);
630
642
  }
631
-
632
- maximum_size = size - offset;
633
643
  }
634
644
 
635
645
  return rb_fiber_scheduler_io_result(total, 0);
646
+ #endif
636
647
  }
637
648
 
638
649
  static
@@ -644,22 +655,53 @@ VALUE io_read_ensure(VALUE _arguments) {
644
655
  return Qnil;
645
656
  }
646
657
 
647
- VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
648
- int descriptor = IO_Event_Selector_io_descriptor(io);
658
+ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
659
+ void *base;
660
+ size_t size;
661
+ rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
662
+
663
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
664
+ size_t offset = NUM2SIZET(_first);
665
+ size_t length = NUM2SIZET(_second);
666
+
667
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
668
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
669
+ } else if (length == 0) {
670
+ return rb_fiber_scheduler_io_result(0, 0);
671
+ }
672
+
673
+ base = (char*)base + offset;
674
+ size = length;
675
+ #else
676
+ size_t length = NUM2SIZET(_first);
677
+ size_t offset = NUM2SIZET(_second);
678
+
679
+ if (offset > size) {
680
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
681
+ } else if (offset == size) {
682
+ return rb_fiber_scheduler_io_result(0, 0);
683
+ }
684
+
685
+ base = (char*)base + offset;
686
+ size -= offset;
687
+ #endif
649
688
 
650
- size_t offset = NUM2SIZET(_offset);
651
- size_t length = NUM2SIZET(_length);
689
+ int descriptor = IO_Event_Selector_io_descriptor(io);
652
690
 
653
691
  struct io_read_arguments io_read_arguments = {
692
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
654
693
  .self = self,
655
694
  .fiber = fiber,
656
695
  .io = io,
696
+ #endif
657
697
 
658
698
  .flags = IO_Event_Selector_nonblock_set(descriptor),
659
699
  .descriptor = descriptor,
660
- .buffer = buffer,
700
+ .base = base,
701
+ .size = size,
702
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
661
703
  .length = length,
662
- .offset = offset,
704
+ #endif
663
705
  };
664
706
 
665
707
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -667,6 +709,7 @@ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
667
709
  return rb_ensure(io_read_loop, (VALUE)&io_read_arguments, io_read_ensure, (VALUE)&io_read_arguments);
668
710
  }
669
711
 
712
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
670
713
  VALUE IO_Event_Selector_EPoll_io_read_compatible(int argc, VALUE *argv, VALUE self)
671
714
  {
672
715
  rb_check_arity(argc, 4, 5);
@@ -679,50 +722,48 @@ VALUE IO_Event_Selector_EPoll_io_read_compatible(int argc, VALUE *argv, VALUE se
679
722
 
680
723
  return IO_Event_Selector_EPoll_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
681
724
  }
725
+ #endif
682
726
 
683
727
  struct io_write_arguments {
728
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
684
729
  VALUE self;
685
730
  VALUE fiber;
686
731
  VALUE io;
732
+ #endif
687
733
 
688
734
  int flags;
689
735
 
690
736
  int descriptor;
691
737
 
692
- VALUE buffer;
738
+ // The remaining readable buffer region after applying the requested offset.
739
+ const void *base;
740
+ size_t size;
741
+
742
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
743
+ // The minimum number of bytes requested by the caller.
693
744
  size_t length;
694
- size_t offset;
745
+ #endif
695
746
  };
696
747
 
697
748
  static
698
749
  VALUE io_write_loop(VALUE _arguments) {
699
750
  struct io_write_arguments *arguments = (struct io_write_arguments *)_arguments;
700
751
 
701
- const void *base;
702
- size_t size;
703
- rb_io_buffer_get_bytes_for_reading(arguments->buffer, &base, &size);
704
-
752
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
753
+ ssize_t result = write(arguments->descriptor, arguments->base, arguments->size);
754
+ return rb_fiber_scheduler_io_result(result, errno);
755
+ #else
705
756
  size_t length = arguments->length;
706
- size_t offset = arguments->offset;
707
757
  size_t total = 0;
708
758
 
709
- if (length > size) {
710
- rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
711
- }
712
-
713
- // Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
714
- if (offset > size) {
715
- return rb_fiber_scheduler_io_result(-1, EINVAL);
716
- }
717
-
718
- size_t maximum_size = size - offset;
759
+ size_t maximum_size = arguments->size;
719
760
  while (maximum_size) {
720
- ssize_t result = write(arguments->descriptor, (char*)base+offset, maximum_size);
761
+ ssize_t result = write(arguments->descriptor, (char*)arguments->base+total, maximum_size);
721
762
 
722
763
  if (result > 0) {
723
764
  total += result;
724
- offset += result;
725
765
  if ((size_t)result >= length) break;
766
+ maximum_size -= result;
726
767
  length -= result;
727
768
  } else if (result == 0) {
728
769
  break;
@@ -731,11 +772,10 @@ VALUE io_write_loop(VALUE _arguments) {
731
772
  } else {
732
773
  return rb_fiber_scheduler_io_result(-1, errno);
733
774
  }
734
-
735
- maximum_size = size - offset;
736
775
  }
737
776
 
738
777
  return rb_fiber_scheduler_io_result(total, 0);
778
+ #endif
739
779
  };
740
780
 
741
781
  static
@@ -747,22 +787,57 @@ VALUE io_write_ensure(VALUE _arguments) {
747
787
  return Qnil;
748
788
  };
749
789
 
750
- VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
751
- int descriptor = IO_Event_Selector_io_descriptor(io);
790
+ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
791
+ const void *base;
792
+ size_t size;
793
+ rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
794
+
795
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
796
+ size_t offset = NUM2SIZET(_first);
797
+ size_t length = NUM2SIZET(_second);
798
+
799
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
800
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
801
+ } else if (length == 0) {
802
+ return rb_fiber_scheduler_io_result(0, 0);
803
+ }
804
+
805
+ base = (const char*)base + offset;
806
+ size = length;
807
+ #else
808
+ size_t length = NUM2SIZET(_first);
809
+ size_t offset = NUM2SIZET(_second);
810
+
811
+ if (length > size) {
812
+ rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
813
+ }
814
+
815
+ if (offset > size) {
816
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
817
+ } else if (offset == size) {
818
+ return rb_fiber_scheduler_io_result(0, 0);
819
+ }
752
820
 
753
- size_t length = NUM2SIZET(_length);
754
- size_t offset = NUM2SIZET(_offset);
821
+ base = (const char*)base + offset;
822
+ size -= offset;
823
+ #endif
824
+
825
+ int descriptor = IO_Event_Selector_io_descriptor(io);
755
826
 
756
827
  struct io_write_arguments io_write_arguments = {
828
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
757
829
  .self = self,
758
830
  .fiber = fiber,
759
831
  .io = io,
832
+ #endif
760
833
 
761
834
  .flags = IO_Event_Selector_nonblock_set(descriptor),
762
835
  .descriptor = descriptor,
763
- .buffer = buffer,
836
+ .base = base,
837
+ .size = size,
838
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
764
839
  .length = length,
765
- .offset = offset,
840
+ #endif
766
841
  };
767
842
 
768
843
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -770,6 +845,7 @@ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
770
845
  return rb_ensure(io_write_loop, (VALUE)&io_write_arguments, io_write_ensure, (VALUE)&io_write_arguments);
771
846
  }
772
847
 
848
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
773
849
  VALUE IO_Event_Selector_EPoll_io_write_compatible(int argc, VALUE *argv, VALUE self)
774
850
  {
775
851
  rb_check_arity(argc, 4, 5);
@@ -782,6 +858,7 @@ VALUE IO_Event_Selector_EPoll_io_write_compatible(int argc, VALUE *argv, VALUE s
782
858
 
783
859
  return IO_Event_Selector_EPoll_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
784
860
  }
861
+ #endif
785
862
 
786
863
  #endif
787
864
 
@@ -809,14 +886,26 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
809
886
  }
810
887
 
811
888
  static
812
- int timeout_nonblocking(struct timespec * timespec) {
889
+ int timeout_is_nonblocking(struct timespec * timespec) {
813
890
  return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
814
891
  }
815
892
 
893
+ // Return true when it is safe and useful to enter the blocking selector wait.
894
+ static
895
+ int select_blocking_allowed(struct timespec * timespec) {
896
+ // A `0` timeout is already a poll. The selector has already performed the immediate poll previously, so there is no useful blocking wait to enter here.
897
+ if (timeout_is_nonblocking(timespec)) {
898
+ return 0;
899
+ }
900
+ return 1;
901
+ }
902
+
816
903
  struct select_arguments {
817
904
  struct IO_Event_Selector_EPoll *selector;
818
905
 
819
906
  int count;
907
+ int result;
908
+ int error;
820
909
  struct epoll_event events[EPOLL_MAX_EVENTS];
821
910
 
822
911
  struct timespec * timeout;
@@ -830,7 +919,7 @@ static int make_timeout_ms(struct timespec * timeout) {
830
919
  return -1;
831
920
  }
832
921
 
833
- if (timeout_nonblocking(timeout)) {
922
+ if (timeout_is_nonblocking(timeout)) {
834
923
  return 0;
835
924
  }
836
925
 
@@ -838,9 +927,9 @@ static int make_timeout_ms(struct timespec * timeout) {
838
927
  }
839
928
 
840
929
  static
841
- int enosys_error(int result) {
930
+ int enosys_error(int result, int error) {
842
931
  if (result == -1) {
843
- return errno == ENOSYS;
932
+ return error == ENOSYS;
844
933
  }
845
934
 
846
935
  return 0;
@@ -851,13 +940,14 @@ void * select_internal(void *_arguments) {
851
940
  struct select_arguments * arguments = (struct select_arguments *)_arguments;
852
941
 
853
942
  #if defined(HAVE_EPOLL_PWAIT2)
854
- arguments->count = epoll_pwait2(arguments->selector->descriptor, arguments->events, EPOLL_MAX_EVENTS, arguments->timeout, NULL);
943
+ arguments->result = epoll_pwait2(arguments->selector->descriptor, arguments->events, arguments->count, arguments->timeout, NULL);
944
+ arguments->error = errno;
855
945
 
856
946
  // Comment out the above line and enable the below lines to test ENOSYS code path.
857
- // arguments->count = -1;
858
- // errno = ENOSYS;
947
+ // arguments->result = -1;
948
+ // arguments->error = ENOSYS;
859
949
 
860
- if (!enosys_error(arguments->count)) {
950
+ if (!enosys_error(arguments->result, arguments->error)) {
861
951
  return NULL;
862
952
  }
863
953
  else {
@@ -865,37 +955,42 @@ void * select_internal(void *_arguments) {
865
955
  }
866
956
  #endif
867
957
 
868
- arguments->count = epoll_wait(arguments->selector->descriptor, arguments->events, EPOLL_MAX_EVENTS, make_timeout_ms(arguments->timeout));
958
+ arguments->result = epoll_wait(arguments->selector->descriptor, arguments->events, arguments->count, make_timeout_ms(arguments->timeout));
959
+ arguments->error = errno;
869
960
 
870
961
  return NULL;
871
962
  }
872
963
 
873
964
  static
874
- void select_internal_without_gvl(struct select_arguments *arguments) {
875
- arguments->selector->blocked = 1;
876
- rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
877
- arguments->selector->blocked = 0;
878
-
879
- if (arguments->count == -1) {
880
- if (errno != EINTR) {
881
- rb_sys_fail("select_internal_without_gvl:epoll_wait");
965
+ int select_internal_without_gvl(struct select_arguments *arguments) {
966
+ arguments->result = -1;
967
+ arguments->error = EINTR;
968
+ IO_Event_Selector_blocking_operation(&arguments->selector->backend, select_internal, (void *)arguments, RUBY_UBF_IO, 0);
969
+
970
+ if (arguments->result == -1) {
971
+ if (arguments->error != EINTR) {
972
+ rb_syserr_fail(arguments->error, "select_internal_without_gvl:epoll_wait");
882
973
  } else {
883
- arguments->count = 0;
974
+ return 0;
884
975
  }
885
976
  }
977
+
978
+ return arguments->result;
886
979
  }
887
980
 
888
981
  static
889
- void select_internal_with_gvl(struct select_arguments *arguments) {
982
+ int select_internal_with_gvl(struct select_arguments *arguments) {
890
983
  select_internal((void *)arguments);
891
984
 
892
- if (arguments->count == -1) {
893
- if (errno != EINTR) {
894
- rb_sys_fail("select_internal_with_gvl:epoll_wait");
985
+ if (arguments->result == -1) {
986
+ if (arguments->error != EINTR) {
987
+ rb_syserr_fail(arguments->error, "select_internal_with_gvl:epoll_wait");
895
988
  } else {
896
- arguments->count = 0;
989
+ return 0;
897
990
  }
898
991
  }
992
+
993
+ return arguments->result;
899
994
  }
900
995
 
901
996
  static
@@ -951,7 +1046,7 @@ VALUE select_handle_events(VALUE _arguments)
951
1046
  struct select_arguments *arguments = (struct select_arguments *)_arguments;
952
1047
  struct IO_Event_Selector_EPoll *selector = arguments->selector;
953
1048
 
954
- for (int i = 0; i < arguments->count; i += 1) {
1049
+ for (int i = 0; i < arguments->result; i += 1) {
955
1050
  const struct epoll_event *event = &arguments->events[i];
956
1051
  if (DEBUG) fprintf(stderr, "-> fd=%d events=%d\n", event->data.fd, event->events);
957
1052
 
@@ -962,7 +1057,7 @@ VALUE select_handle_events(VALUE _arguments)
962
1057
  }
963
1058
  }
964
1059
 
965
- return INT2NUM(arguments->count);
1060
+ return INT2NUM(arguments->result);
966
1061
  }
967
1062
 
968
1063
  static
@@ -987,6 +1082,8 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
987
1082
 
988
1083
  struct select_arguments arguments = {
989
1084
  .selector = selector,
1085
+ .count = EPOLL_MAX_EVENTS,
1086
+ .result = 0,
990
1087
  .storage = {
991
1088
  .tv_sec = 0,
992
1089
  .tv_nsec = 0
@@ -997,22 +1094,22 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
997
1094
  arguments.timeout = &arguments.storage;
998
1095
 
999
1096
  // Process any currently pending events:
1000
- select_internal_with_gvl(&arguments);
1097
+ int result = select_internal_with_gvl(&arguments);
1001
1098
 
1002
1099
  // If we:
1003
1100
  // 1. Didn't process any ready fibers, and
1004
1101
  // 2. Didn't process any events from non-blocking select (above), and
1005
1102
  // 3. There are no items in the ready list,
1006
1103
  // then we can perform a blocking select.
1007
- if (!ready && !arguments.count && !selector->backend.ready) {
1104
+ if (!ready && !result && !selector->backend.ready) {
1008
1105
  arguments.timeout = make_timeout(duration, &arguments.storage);
1009
1106
 
1010
- if (!timeout_nonblocking(arguments.timeout)) {
1107
+ if (select_blocking_allowed(arguments.timeout)) {
1011
1108
  struct timespec start_time;
1012
1109
  IO_Event_Time_current(&start_time);
1013
1110
 
1014
1111
  // Wait for events to occur:
1015
- select_internal_without_gvl(&arguments);
1112
+ result = select_internal_without_gvl(&arguments);
1016
1113
 
1017
1114
  struct timespec end_time;
1018
1115
  IO_Event_Time_current(&end_time);
@@ -1020,7 +1117,7 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
1020
1117
  }
1021
1118
  }
1022
1119
 
1023
- if (arguments.count) {
1120
+ if (result) {
1024
1121
  return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
1025
1122
  } else {
1026
1123
  return RB_INT2NUM(0);
@@ -1032,7 +1129,7 @@ VALUE IO_Event_Selector_EPoll_wakeup(VALUE self) {
1032
1129
  TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
1033
1130
 
1034
1131
  // If we are blocking, we can schedule a nop event to wake up the selector:
1035
- if (selector->blocked) {
1132
+ if (selector->backend.blocked) {
1036
1133
  IO_Event_Interrupt_signal(&selector->interrupt);
1037
1134
 
1038
1135
  return Qtrue;
@@ -1079,17 +1176,19 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
1079
1176
  rb_define_method(IO_Event_Selector_EPoll, "select", IO_Event_Selector_EPoll_select, 1);
1080
1177
  rb_define_method(IO_Event_Selector_EPoll, "wakeup", IO_Event_Selector_EPoll_wakeup, 0);
1081
1178
  rb_define_method(IO_Event_Selector_EPoll, "close", IO_Event_Selector_EPoll_close, 0);
1179
+ rb_define_method(IO_Event_Selector_EPoll, "closed?", IO_Event_Selector_EPoll_closed_p, 0);
1082
1180
 
1083
1181
  rb_define_method(IO_Event_Selector_EPoll, "io_wait", IO_Event_Selector_EPoll_io_wait, 3);
1084
1182
 
1085
1183
  #ifdef HAVE_RUBY_IO_BUFFER_H
1184
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1185
+ rb_define_method(IO_Event_Selector_EPoll, "io_read", IO_Event_Selector_EPoll_io_read, 5);
1186
+ rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write, 5);
1187
+ #else
1086
1188
  rb_define_method(IO_Event_Selector_EPoll, "io_read", IO_Event_Selector_EPoll_io_read_compatible, -1);
1087
1189
  rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write_compatible, -1);
1088
1190
  #endif
1089
-
1090
- // Once compatibility isn't a concern, we can do this:
1091
- // rb_define_method(IO_Event_Selector_EPoll, "io_read", IO_Event_Selector_EPoll_io_read, 5);
1092
- // rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write, 5);
1191
+ #endif
1093
1192
 
1094
1193
  rb_define_method(IO_Event_Selector_EPoll, "process_wait", IO_Event_Selector_EPoll_process_wait, 3);
1095
1194
  }