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.
@@ -12,6 +12,7 @@
12
12
  #include <errno.h>
13
13
  #include <sys/wait.h>
14
14
  #include <signal.h>
15
+ #include <unistd.h>
15
16
 
16
17
  #include "../interrupt.h"
17
18
 
@@ -47,11 +48,7 @@ struct IO_Event_Selector_KQueue
47
48
  {
48
49
  struct IO_Event_Selector backend;
49
50
  int descriptor;
50
-
51
- // Flag indicating whether the selector is currently blocked in a system call.
52
- // Set to 1 when blocked in kevent() without GVL, 0 otherwise.
53
- // Used by wakeup() to determine if an interrupt signal is needed.
54
- int blocked;
51
+ pid_t owner;
55
52
 
56
53
  struct timespec idle_duration;
57
54
 
@@ -132,7 +129,14 @@ static
132
129
  void close_internal(struct IO_Event_Selector_KQueue *selector)
133
130
  {
134
131
  if (selector->descriptor >= 0) {
135
- close(selector->descriptor);
132
+ if (selector->owner == getpid()) {
133
+ close(selector->descriptor);
134
+
135
+ #ifdef IO_EVENT_SELECTOR_KQUEUE_USE_INTERRUPT
136
+ IO_Event_Interrupt_close(&selector->interrupt);
137
+ #endif
138
+ }
139
+
136
140
  selector->descriptor = -1;
137
141
  }
138
142
  }
@@ -289,8 +293,7 @@ VALUE IO_Event_Selector_KQueue_allocate(VALUE self) {
289
293
 
290
294
  IO_Event_Selector_initialize(&selector->backend, self, Qnil);
291
295
  selector->descriptor = -1;
292
- selector->blocked = 0;
293
-
296
+ selector->owner = 0;
294
297
  selector->descriptors.element_initialize = IO_Event_Selector_KQueue_Descriptor_initialize;
295
298
  selector->descriptors.element_free = IO_Event_Selector_KQueue_Descriptor_free;
296
299
 
@@ -331,6 +334,7 @@ VALUE IO_Event_Selector_KQueue_initialize(VALUE self, VALUE loop) {
331
334
  ioctl(result, FIOCLEX);
332
335
 
333
336
  selector->descriptor = result;
337
+ selector->owner = getpid();
334
338
 
335
339
  rb_update_max_fd(selector->descriptor);
336
340
  }
@@ -365,13 +369,16 @@ VALUE IO_Event_Selector_KQueue_close(VALUE self) {
365
369
 
366
370
  close_internal(selector);
367
371
 
368
- #ifdef IO_EVENT_SELECTOR_KQUEUE_USE_INTERRUPT
369
- IO_Event_Interrupt_close(&selector->interrupt);
370
- #endif
371
-
372
372
  return Qnil;
373
373
  }
374
374
 
375
+ VALUE IO_Event_Selector_KQueue_closed_p(VALUE self) {
376
+ struct IO_Event_Selector_KQueue *selector = NULL;
377
+ TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
378
+
379
+ return selector->descriptor < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
380
+ }
381
+
375
382
  VALUE IO_Event_Selector_KQueue_transfer(VALUE self)
376
383
  {
377
384
  struct IO_Event_Selector_KQueue *selector = NULL;
@@ -455,7 +462,7 @@ VALUE process_wait_transfer(VALUE _arguments) {
455
462
 
456
463
  if (arguments->waiting->ready) {
457
464
  process_prewait(arguments->pid);
458
- return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
465
+ return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
459
466
  } else {
460
467
  return Qfalse;
461
468
  }
@@ -479,6 +486,11 @@ VALUE IO_Event_Selector_KQueue_process_wait(VALUE self, VALUE fiber, VALUE _pid,
479
486
  pid_t pid = NUM2PIDT(_pid);
480
487
  int flags = NUM2INT(_flags);
481
488
 
489
+ // `EVFILT_PROC` can only refer to a specific process, so waiting for any child or a process group (pid <= 0) is delegated to the threaded fallback:
490
+ if (pid <= 0) {
491
+ return IO_Event_Selector_process_wait(pid, flags);
492
+ }
493
+
482
494
  struct IO_Event_Selector_KQueue_Waiting waiting = {
483
495
  .list = {.type = &IO_Event_Selector_KQueue_process_wait_list_type},
484
496
  .fiber = fiber,
@@ -500,7 +512,7 @@ VALUE IO_Event_Selector_KQueue_process_wait(VALUE self, VALUE fiber, VALUE _pid,
500
512
  if (errno == ESRCH) {
501
513
  process_prewait(pid);
502
514
 
503
- return IO_Event_Selector_process_status_wait(pid, flags);
515
+ return IO_Event_Selector_process_status_reap(pid, flags);
504
516
  }
505
517
 
506
518
  rb_sys_fail("IO_Event_Selector_KQueue_process_wait:IO_Event_Selector_KQueue_Waiting_register");
@@ -570,48 +582,49 @@ VALUE IO_Event_Selector_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE
570
582
  #ifdef HAVE_RUBY_IO_BUFFER_H
571
583
 
572
584
  struct io_read_arguments {
585
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
573
586
  VALUE self;
574
587
  VALUE fiber;
575
588
  VALUE io;
589
+ #endif
576
590
 
577
591
  int flags;
578
592
 
579
593
  int descriptor;
580
594
 
581
- VALUE buffer;
595
+ // The remaining writable buffer region after applying the requested offset.
596
+ void *base;
597
+ size_t size;
598
+
599
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
600
+ // The minimum number of bytes requested by the caller.
582
601
  size_t length;
583
- size_t offset;
602
+ #endif
584
603
  };
585
604
 
586
605
  static
587
606
  VALUE io_read_loop(VALUE _arguments) {
588
607
  struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
589
608
 
590
- void *base;
591
- size_t size;
592
- rb_io_buffer_get_bytes_for_writing(arguments->buffer, &base, &size);
593
-
609
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
610
+ ssize_t result = read(arguments->descriptor, arguments->base, arguments->size);
611
+ return rb_fiber_scheduler_io_result(result, errno);
612
+ #else
594
613
  size_t length = arguments->length;
595
- size_t offset = arguments->offset;
596
614
  size_t total = 0;
597
615
 
598
616
  if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu)\n", arguments->descriptor, length);
599
617
 
600
- // Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
601
- if (offset > size) {
602
- return rb_fiber_scheduler_io_result(-1, EINVAL);
603
- }
604
-
605
- size_t maximum_size = size - offset;
618
+ size_t maximum_size = arguments->size;
606
619
  while (maximum_size) {
607
- if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld)\n", arguments->descriptor, offset, maximum_size);
608
- ssize_t result = read(arguments->descriptor, (char*)base+offset, maximum_size);
609
- if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld) -> %zd\n", arguments->descriptor, offset, maximum_size, result);
620
+ if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld)\n", arguments->descriptor, total, maximum_size);
621
+ ssize_t result = read(arguments->descriptor, (char*)arguments->base+total, maximum_size);
622
+ if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld) -> %zd\n", arguments->descriptor, total, maximum_size, result);
610
623
 
611
624
  if (result > 0) {
612
625
  total += result;
613
- offset += result;
614
626
  if ((size_t)result >= length) break;
627
+ maximum_size -= result;
615
628
  length -= result;
616
629
  } else if (result == 0) {
617
630
  break;
@@ -622,12 +635,11 @@ VALUE io_read_loop(VALUE _arguments) {
622
635
  if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> errno=%d\n", arguments->descriptor, length, errno);
623
636
  return rb_fiber_scheduler_io_result(-1, errno);
624
637
  }
625
-
626
- maximum_size = size - offset;
627
638
  }
628
639
 
629
- if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, offset);
640
+ if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, total);
630
641
  return rb_fiber_scheduler_io_result(total, 0);
642
+ #endif
631
643
  }
632
644
 
633
645
  static
@@ -639,25 +651,56 @@ VALUE io_read_ensure(VALUE _arguments) {
639
651
  return Qnil;
640
652
  }
641
653
 
642
- VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
654
+ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
643
655
  struct IO_Event_Selector_KQueue *selector = NULL;
644
656
  TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
645
657
 
646
- int descriptor = IO_Event_Selector_io_descriptor(io);
658
+ void *base;
659
+ size_t size;
660
+ rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
661
+
662
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
663
+ size_t offset = NUM2SIZET(_first);
664
+ size_t length = NUM2SIZET(_second);
665
+
666
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
667
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
668
+ } else if (length == 0) {
669
+ return rb_fiber_scheduler_io_result(0, 0);
670
+ }
671
+
672
+ base = (char*)base + offset;
673
+ size = length;
674
+ #else
675
+ size_t length = NUM2SIZET(_first);
676
+ size_t offset = NUM2SIZET(_second);
647
677
 
648
- size_t length = NUM2SIZET(_length);
649
- size_t offset = NUM2SIZET(_offset);
678
+ if (offset > size) {
679
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
680
+ } else if (offset == size) {
681
+ return rb_fiber_scheduler_io_result(0, 0);
682
+ }
683
+
684
+ base = (char*)base + offset;
685
+ size -= offset;
686
+ #endif
687
+
688
+ int descriptor = IO_Event_Selector_io_descriptor(io);
650
689
 
651
690
  struct io_read_arguments io_read_arguments = {
691
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
652
692
  .self = self,
653
693
  .fiber = fiber,
654
694
  .io = io,
695
+ #endif
655
696
 
656
697
  .flags = IO_Event_Selector_nonblock_set(descriptor),
657
698
  .descriptor = descriptor,
658
- .buffer = buffer,
699
+ .base = base,
700
+ .size = size,
701
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
659
702
  .length = length,
660
- .offset = offset,
703
+ #endif
661
704
  };
662
705
 
663
706
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -665,6 +708,7 @@ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE
665
708
  return rb_ensure(io_read_loop, (VALUE)&io_read_arguments, io_read_ensure, (VALUE)&io_read_arguments);
666
709
  }
667
710
 
711
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
668
712
  static VALUE IO_Event_Selector_KQueue_io_read_compatible(int argc, VALUE *argv, VALUE self)
669
713
  {
670
714
  rb_check_arity(argc, 4, 5);
@@ -677,54 +721,52 @@ static VALUE IO_Event_Selector_KQueue_io_read_compatible(int argc, VALUE *argv,
677
721
 
678
722
  return IO_Event_Selector_KQueue_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
679
723
  }
724
+ #endif
680
725
 
681
726
  struct io_write_arguments {
727
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
682
728
  VALUE self;
683
729
  VALUE fiber;
684
730
  VALUE io;
731
+ #endif
685
732
 
686
733
  int flags;
687
734
 
688
735
  int descriptor;
689
736
 
690
- VALUE buffer;
737
+ // The remaining readable buffer region after applying the requested offset.
738
+ const void *base;
739
+ size_t size;
740
+
741
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
742
+ // The minimum number of bytes requested by the caller.
691
743
  size_t length;
692
- size_t offset;
744
+ #endif
693
745
  };
694
746
 
695
747
  static
696
748
  VALUE io_write_loop(VALUE _arguments) {
697
749
  struct io_write_arguments *arguments = (struct io_write_arguments *)_arguments;
698
750
 
699
- const void *base;
700
- size_t size;
701
- rb_io_buffer_get_bytes_for_reading(arguments->buffer, &base, &size);
702
-
751
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
752
+ ssize_t result = write(arguments->descriptor, arguments->base, arguments->size);
753
+ return rb_fiber_scheduler_io_result(result, errno);
754
+ #else
703
755
  size_t length = arguments->length;
704
- size_t offset = arguments->offset;
705
756
  size_t total = 0;
706
757
 
707
- if (length > size) {
708
- rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
709
- }
710
-
711
758
  if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu)\n", arguments->descriptor, length);
712
759
 
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;
760
+ size_t maximum_size = arguments->size;
719
761
  while (maximum_size) {
720
- if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld, length=%zu)\n", arguments->descriptor, offset, maximum_size, length);
721
- ssize_t result = write(arguments->descriptor, (char*)base+offset, maximum_size);
722
- if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld) -> %zd\n", arguments->descriptor, offset, maximum_size, result);
762
+ if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld, length=%zu)\n", arguments->descriptor, total, maximum_size, length);
763
+ ssize_t result = write(arguments->descriptor, (char*)arguments->base+total, maximum_size);
764
+ if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld) -> %zd\n", arguments->descriptor, total, maximum_size, result);
723
765
 
724
766
  if (result > 0) {
725
767
  total += result;
726
- offset += result;
727
768
  if ((size_t)result >= length) break;
769
+ maximum_size -= result;
728
770
  length -= result;
729
771
  } else if (result == 0) {
730
772
  break;
@@ -735,12 +777,11 @@ VALUE io_write_loop(VALUE _arguments) {
735
777
  if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> errno=%d\n", arguments->descriptor, length, errno);
736
778
  return rb_fiber_scheduler_io_result(-1, errno);
737
779
  }
738
-
739
- maximum_size = size - offset;
740
780
  }
741
781
 
742
- if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, offset);
782
+ if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, total);
743
783
  return rb_fiber_scheduler_io_result(total, 0);
784
+ #endif
744
785
  };
745
786
 
746
787
  static
@@ -752,25 +793,60 @@ VALUE io_write_ensure(VALUE _arguments) {
752
793
  return Qnil;
753
794
  };
754
795
 
755
- VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
796
+ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
756
797
  struct IO_Event_Selector_KQueue *selector = NULL;
757
798
  TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
758
799
 
759
- int descriptor = IO_Event_Selector_io_descriptor(io);
800
+ const void *base;
801
+ size_t size;
802
+ rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
760
803
 
761
- size_t length = NUM2SIZET(_length);
762
- size_t offset = NUM2SIZET(_offset);
804
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
805
+ size_t offset = NUM2SIZET(_first);
806
+ size_t length = NUM2SIZET(_second);
807
+
808
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
809
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
810
+ } else if (length == 0) {
811
+ return rb_fiber_scheduler_io_result(0, 0);
812
+ }
813
+
814
+ base = (const char*)base + offset;
815
+ size = length;
816
+ #else
817
+ size_t length = NUM2SIZET(_first);
818
+ size_t offset = NUM2SIZET(_second);
819
+
820
+ if (length > size) {
821
+ rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
822
+ }
823
+
824
+ if (offset > size) {
825
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
826
+ } else if (offset == size) {
827
+ return rb_fiber_scheduler_io_result(0, 0);
828
+ }
829
+
830
+ base = (const char*)base + offset;
831
+ size -= offset;
832
+ #endif
833
+
834
+ int descriptor = IO_Event_Selector_io_descriptor(io);
763
835
 
764
836
  struct io_write_arguments io_write_arguments = {
837
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
765
838
  .self = self,
766
839
  .fiber = fiber,
767
840
  .io = io,
841
+ #endif
768
842
 
769
843
  .flags = IO_Event_Selector_nonblock_set(descriptor),
770
844
  .descriptor = descriptor,
771
- .buffer = buffer,
845
+ .base = base,
846
+ .size = size,
847
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
772
848
  .length = length,
773
- .offset = offset,
849
+ #endif
774
850
  };
775
851
 
776
852
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -778,6 +854,7 @@ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
778
854
  return rb_ensure(io_write_loop, (VALUE)&io_write_arguments, io_write_ensure, (VALUE)&io_write_arguments);
779
855
  }
780
856
 
857
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
781
858
  static VALUE IO_Event_Selector_KQueue_io_write_compatible(int argc, VALUE *argv, VALUE self)
782
859
  {
783
860
  rb_check_arity(argc, 4, 5);
@@ -790,6 +867,7 @@ static VALUE IO_Event_Selector_KQueue_io_write_compatible(int argc, VALUE *argv,
790
867
 
791
868
  return IO_Event_Selector_KQueue_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
792
869
  }
870
+ #endif
793
871
 
794
872
  #endif
795
873
 
@@ -816,15 +894,22 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
816
894
  return storage;
817
895
  }
818
896
 
897
+ // Return true when it is safe and useful to enter the blocking selector wait.
819
898
  static
820
- int timeout_nonblocking(struct timespec * timespec) {
821
- return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
899
+ int select_blocking_allowed(struct timespec * timespec) {
900
+ // 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.
901
+ if (timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0) {
902
+ return 0;
903
+ }
904
+ return 1;
822
905
  }
823
906
 
824
907
  struct select_arguments {
825
908
  struct IO_Event_Selector_KQueue *selector;
826
909
 
827
910
  int count;
911
+ int result;
912
+ int error;
828
913
  struct kevent events[KQUEUE_MAX_EVENTS];
829
914
 
830
915
  struct timespec storage;
@@ -837,38 +922,42 @@ static
837
922
  void * select_internal(void *_arguments) {
838
923
  struct select_arguments * arguments = (struct select_arguments *)_arguments;
839
924
 
840
- arguments->count = kevent(arguments->selector->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
925
+ arguments->result = kevent(arguments->selector->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
926
+ arguments->error = errno;
841
927
 
842
928
  return NULL;
843
929
  }
844
930
 
845
931
  static
846
- void select_internal_without_gvl(struct select_arguments *arguments) {
847
- arguments->selector->blocked = 1;
848
-
849
- rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
850
- arguments->selector->blocked = 0;
851
-
852
- if (arguments->count == -1) {
853
- if (errno != EINTR) {
854
- rb_sys_fail("select_internal_without_gvl:kevent");
932
+ int select_internal_without_gvl(struct select_arguments *arguments) {
933
+ arguments->result = -1;
934
+ arguments->error = EINTR;
935
+ IO_Event_Selector_blocking_operation(&arguments->selector->backend, select_internal, (void *)arguments, RUBY_UBF_IO, 0);
936
+
937
+ if (arguments->result == -1) {
938
+ if (arguments->error != EINTR) {
939
+ rb_syserr_fail(arguments->error, "select_internal_without_gvl:kevent");
855
940
  } else {
856
- arguments->count = 0;
941
+ return 0;
857
942
  }
858
943
  }
944
+
945
+ return arguments->result;
859
946
  }
860
947
 
861
948
  static
862
- void select_internal_with_gvl(struct select_arguments *arguments) {
949
+ int select_internal_with_gvl(struct select_arguments *arguments) {
863
950
  select_internal((void *)arguments);
864
951
 
865
- if (arguments->count == -1) {
866
- if (errno != EINTR) {
867
- rb_sys_fail("select_internal_with_gvl:kevent");
952
+ if (arguments->result == -1) {
953
+ if (arguments->error != EINTR) {
954
+ rb_syserr_fail(arguments->error, "select_internal_with_gvl:kevent");
868
955
  } else {
869
- arguments->count = 0;
956
+ return 0;
870
957
  }
871
958
  }
959
+
960
+ return arguments->result;
872
961
  }
873
962
 
874
963
  static
@@ -922,14 +1011,14 @@ VALUE select_handle_events(VALUE _arguments)
922
1011
  struct select_arguments *arguments = (struct select_arguments *)_arguments;
923
1012
  struct IO_Event_Selector_KQueue *selector = arguments->selector;
924
1013
 
925
- for (int i = 0; i < arguments->count; i += 1) {
1014
+ for (int i = 0; i < arguments->result; i += 1) {
926
1015
  if (arguments->events[i].udata) {
927
1016
  struct IO_Event_Selector_KQueue_Descriptor *kqueue_descriptor = arguments->events[i].udata;
928
1017
  kqueue_descriptor->ready_events |= events_from_kevent_filter(arguments->events[i].filter);
929
1018
  }
930
1019
  }
931
1020
 
932
- for (int i = 0; i < arguments->count; i += 1) {
1021
+ for (int i = 0; i < arguments->result; i += 1) {
933
1022
  if (arguments->events[i].udata) {
934
1023
  struct IO_Event_Selector_KQueue_Descriptor *kqueue_descriptor = arguments->events[i].udata;
935
1024
  IO_Event_Selector_KQueue_handle(selector, arguments->events[i].ident, kqueue_descriptor, &arguments->saved);
@@ -940,7 +1029,7 @@ VALUE select_handle_events(VALUE _arguments)
940
1029
  }
941
1030
  }
942
1031
 
943
- return RB_INT2NUM(arguments->count);
1032
+ return RB_INT2NUM(arguments->result);
944
1033
  }
945
1034
 
946
1035
  static
@@ -965,6 +1054,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
965
1054
  struct select_arguments arguments = {
966
1055
  .selector = selector,
967
1056
  .count = KQUEUE_MAX_EVENTS,
1057
+ .result = 0,
968
1058
  .storage = {
969
1059
  .tv_sec = 0,
970
1060
  .tv_nsec = 0
@@ -975,14 +1065,14 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
975
1065
  arguments.timeout = &arguments.storage;
976
1066
 
977
1067
  // We break this implementation into two parts.
978
- // (1) count = kevent(..., timeout = 0)
979
- // (2) without gvl: kevent(..., timeout = 0) if count == 0 and timeout != 0
1068
+ // (1) result = kevent(..., timeout = 0)
1069
+ // (2) without gvl: kevent(..., timeout = 0) if result == 0 and timeout != 0
980
1070
  // This allows us to avoid releasing and reacquiring the GVL.
981
1071
  // Non-comprehensive testing shows this gives a 1.5x speedup.
982
1072
 
983
1073
  // First do the syscall with no timeout to get any immediately available events:
984
1074
  if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl timeout=" IO_EVENT_TIME_PRINTF_TIMESPEC "\r\n", IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
985
- select_internal_with_gvl(&arguments);
1075
+ int result = select_internal_with_gvl(&arguments);
986
1076
  if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl done\r\n");
987
1077
 
988
1078
  // If we:
@@ -990,17 +1080,15 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
990
1080
  // 2. Didn't process any events from non-blocking select (above), and
991
1081
  // 3. There are no items in the ready list,
992
1082
  // then we can perform a blocking select.
993
- if (!ready && !arguments.count && !selector->backend.ready) {
1083
+ if (!ready && !result && !selector->backend.ready) {
994
1084
  arguments.timeout = make_timeout(duration, &arguments.storage);
995
1085
 
996
- if (!timeout_nonblocking(arguments.timeout)) {
997
- arguments.count = KQUEUE_MAX_EVENTS;
998
-
1086
+ if (select_blocking_allowed(arguments.timeout)) {
999
1087
  struct timespec start_time;
1000
1088
  IO_Event_Time_current(&start_time);
1001
1089
 
1002
1090
  if (DEBUG) fprintf(stderr, "IO_Event_Selector_KQueue_select timeout=" IO_EVENT_TIME_PRINTF_TIMESPEC "\n", IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
1003
- select_internal_without_gvl(&arguments);
1091
+ result = select_internal_without_gvl(&arguments);
1004
1092
 
1005
1093
  struct timespec end_time;
1006
1094
  IO_Event_Time_current(&end_time);
@@ -1008,7 +1096,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
1008
1096
  }
1009
1097
  }
1010
1098
 
1011
- if (arguments.count) {
1099
+ if (result) {
1012
1100
  return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
1013
1101
  } else {
1014
1102
  return RB_INT2NUM(0);
@@ -1019,7 +1107,7 @@ VALUE IO_Event_Selector_KQueue_wakeup(VALUE self) {
1019
1107
  struct IO_Event_Selector_KQueue *selector = NULL;
1020
1108
  TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
1021
1109
 
1022
- if (selector->blocked) {
1110
+ if (selector->backend.blocked) {
1023
1111
  #ifdef IO_EVENT_SELECTOR_KQUEUE_USE_INTERRUPT
1024
1112
  IO_Event_Interrupt_signal(&selector->interrupt);
1025
1113
  #else
@@ -1090,12 +1178,18 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
1090
1178
  rb_define_method(IO_Event_Selector_KQueue, "select", IO_Event_Selector_KQueue_select, 1);
1091
1179
  rb_define_method(IO_Event_Selector_KQueue, "wakeup", IO_Event_Selector_KQueue_wakeup, 0);
1092
1180
  rb_define_method(IO_Event_Selector_KQueue, "close", IO_Event_Selector_KQueue_close, 0);
1181
+ rb_define_method(IO_Event_Selector_KQueue, "closed?", IO_Event_Selector_KQueue_closed_p, 0);
1093
1182
 
1094
1183
  rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
1095
1184
 
1096
1185
  #ifdef HAVE_RUBY_IO_BUFFER_H
1186
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1187
+ rb_define_method(IO_Event_Selector_KQueue, "io_read", IO_Event_Selector_KQueue_io_read, 5);
1188
+ rb_define_method(IO_Event_Selector_KQueue, "io_write", IO_Event_Selector_KQueue_io_write, 5);
1189
+ #else
1097
1190
  rb_define_method(IO_Event_Selector_KQueue, "io_read", IO_Event_Selector_KQueue_io_read_compatible, -1);
1098
1191
  rb_define_method(IO_Event_Selector_KQueue, "io_write", IO_Event_Selector_KQueue_io_write_compatible, -1);
1192
+ #endif
1099
1193
  #endif
1100
1194
 
1101
1195
  rb_define_method(IO_Event_Selector_KQueue, "process_wait", IO_Event_Selector_KQueue_process_wait, 3);