io-event 1.16.4 → 1.18.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/io/event/selector/epoll.c +123 -70
- data/ext/io/event/selector/kqueue.c +126 -80
- data/ext/io/event/selector/selector.c +4 -0
- data/ext/io/event/selector/selector.h +6 -0
- data/ext/io/event/selector/uring.c +77 -31
- data/lib/io/event/debug/selector.rb +5 -0
- data/lib/io/event/selector/select.rb +10 -0
- data/lib/io/event/version.rb +1 -1
- data/readme.md +8 -8
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ad9ab9326ef0bb4d637833e5d9705940a7d150c0e42154ee4642fbf6f4b4b49
|
|
4
|
+
data.tar.gz: bb1274afaa893df427b3eb422aa5c198da08493126e8b5f3ee2daac375f1ebda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 543bdb171056e37849b7042c0d822e7421908bbd006227be92b1b2c5c12d7988c8201ce02316c9f0aa32df9746f4a6d88345c8eb9f38f9c5018f4adbaeca27b9
|
|
7
|
+
data.tar.gz: 65b3f2b61cf38b3c66cb2d0e9299ece198e546d0e30676f8bbd616fdccb043b47f032f0e8fd00bb5bf14bdef471f5ebb1c4769e8de86654900916fc8952c2be7
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -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,6 +39,7 @@ struct IO_Event_Selector_EPoll
|
|
|
38
39
|
{
|
|
39
40
|
struct IO_Event_Selector backend;
|
|
40
41
|
int descriptor;
|
|
42
|
+
pid_t owner;
|
|
41
43
|
|
|
42
44
|
// Flag indicating whether the selector is currently blocked in a system call.
|
|
43
45
|
// Set to 1 when blocked in epoll_wait() without GVL, 0 otherwise.
|
|
@@ -131,10 +133,12 @@ static
|
|
|
131
133
|
void close_internal(struct IO_Event_Selector_EPoll *selector)
|
|
132
134
|
{
|
|
133
135
|
if (selector->descriptor >= 0) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
if (selector->owner == getpid()) {
|
|
137
|
+
close(selector->descriptor);
|
|
138
|
+
IO_Event_Interrupt_close(&selector->interrupt);
|
|
139
|
+
}
|
|
136
140
|
|
|
137
|
-
|
|
141
|
+
selector->descriptor = -1;
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|
|
@@ -315,6 +319,7 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
|
|
|
315
319
|
|
|
316
320
|
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
317
321
|
selector->descriptor = -1;
|
|
322
|
+
selector->owner = 0;
|
|
318
323
|
selector->blocked = 0;
|
|
319
324
|
|
|
320
325
|
selector->descriptors.element_initialize = IO_Event_Selector_EPoll_Descriptor_initialize;
|
|
@@ -350,6 +355,7 @@ VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
|
|
|
350
355
|
rb_sys_fail("IO_Event_Selector_EPoll_initialize:epoll_create");
|
|
351
356
|
} else {
|
|
352
357
|
selector->descriptor = result;
|
|
358
|
+
selector->owner = getpid();
|
|
353
359
|
|
|
354
360
|
rb_update_max_fd(selector->descriptor);
|
|
355
361
|
}
|
|
@@ -385,6 +391,13 @@ VALUE IO_Event_Selector_EPoll_close(VALUE self) {
|
|
|
385
391
|
return Qnil;
|
|
386
392
|
}
|
|
387
393
|
|
|
394
|
+
VALUE IO_Event_Selector_EPoll_closed_p(VALUE self) {
|
|
395
|
+
struct IO_Event_Selector_EPoll *selector = NULL;
|
|
396
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
|
|
397
|
+
|
|
398
|
+
return selector->descriptor < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
|
|
399
|
+
}
|
|
400
|
+
|
|
388
401
|
VALUE IO_Event_Selector_EPoll_transfer(VALUE self)
|
|
389
402
|
{
|
|
390
403
|
struct IO_Event_Selector_EPoll *selector = NULL;
|
|
@@ -590,36 +603,29 @@ struct io_read_arguments {
|
|
|
590
603
|
|
|
591
604
|
int descriptor;
|
|
592
605
|
|
|
593
|
-
|
|
606
|
+
// The remaining writable buffer region after applying the requested offset.
|
|
607
|
+
void *base;
|
|
608
|
+
size_t size;
|
|
609
|
+
|
|
610
|
+
// The minimum number of bytes requested by the caller.
|
|
594
611
|
size_t length;
|
|
595
|
-
size_t offset;
|
|
596
612
|
};
|
|
597
613
|
|
|
598
614
|
static
|
|
599
615
|
VALUE io_read_loop(VALUE _arguments) {
|
|
600
616
|
struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
|
|
601
617
|
|
|
602
|
-
void *base;
|
|
603
|
-
size_t size;
|
|
604
|
-
rb_io_buffer_get_bytes_for_writing(arguments->buffer, &base, &size);
|
|
605
|
-
|
|
606
618
|
size_t length = arguments->length;
|
|
607
|
-
size_t offset = arguments->offset;
|
|
608
619
|
size_t total = 0;
|
|
609
620
|
|
|
610
|
-
|
|
611
|
-
if (offset > size) {
|
|
612
|
-
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
size_t maximum_size = size - offset;
|
|
621
|
+
size_t maximum_size = arguments->size;
|
|
616
622
|
while (maximum_size) {
|
|
617
|
-
ssize_t result = read(arguments->descriptor, (char*)base+
|
|
623
|
+
ssize_t result = read(arguments->descriptor, (char*)arguments->base+total, maximum_size);
|
|
618
624
|
|
|
619
625
|
if (result > 0) {
|
|
620
626
|
total += result;
|
|
621
|
-
offset += result;
|
|
622
627
|
if ((size_t)result >= length) break;
|
|
628
|
+
maximum_size -= result;
|
|
623
629
|
length -= result;
|
|
624
630
|
} else if (result == 0) {
|
|
625
631
|
break;
|
|
@@ -628,8 +634,6 @@ VALUE io_read_loop(VALUE _arguments) {
|
|
|
628
634
|
} else {
|
|
629
635
|
return rb_fiber_scheduler_io_result(-1, errno);
|
|
630
636
|
}
|
|
631
|
-
|
|
632
|
-
maximum_size = size - offset;
|
|
633
637
|
}
|
|
634
638
|
|
|
635
639
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
@@ -645,11 +649,24 @@ VALUE io_read_ensure(VALUE _arguments) {
|
|
|
645
649
|
}
|
|
646
650
|
|
|
647
651
|
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);
|
|
649
|
-
|
|
650
652
|
size_t offset = NUM2SIZET(_offset);
|
|
651
653
|
size_t length = NUM2SIZET(_length);
|
|
652
654
|
|
|
655
|
+
void *base;
|
|
656
|
+
size_t size;
|
|
657
|
+
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
658
|
+
|
|
659
|
+
if (offset > size) {
|
|
660
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
661
|
+
} else if (offset == size) {
|
|
662
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
base = (char*)base + offset;
|
|
666
|
+
size -= offset;
|
|
667
|
+
|
|
668
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
669
|
+
|
|
653
670
|
struct io_read_arguments io_read_arguments = {
|
|
654
671
|
.self = self,
|
|
655
672
|
.fiber = fiber,
|
|
@@ -657,9 +674,9 @@ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
657
674
|
|
|
658
675
|
.flags = IO_Event_Selector_nonblock_set(descriptor),
|
|
659
676
|
.descriptor = descriptor,
|
|
660
|
-
.
|
|
677
|
+
.base = base,
|
|
678
|
+
.size = size,
|
|
661
679
|
.length = length,
|
|
662
|
-
.offset = offset,
|
|
663
680
|
};
|
|
664
681
|
|
|
665
682
|
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
@@ -689,40 +706,29 @@ struct io_write_arguments {
|
|
|
689
706
|
|
|
690
707
|
int descriptor;
|
|
691
708
|
|
|
692
|
-
|
|
709
|
+
// The remaining readable buffer region after applying the requested offset.
|
|
710
|
+
const void *base;
|
|
711
|
+
size_t size;
|
|
712
|
+
|
|
713
|
+
// The minimum number of bytes requested by the caller.
|
|
693
714
|
size_t length;
|
|
694
|
-
size_t offset;
|
|
695
715
|
};
|
|
696
716
|
|
|
697
717
|
static
|
|
698
718
|
VALUE io_write_loop(VALUE _arguments) {
|
|
699
719
|
struct io_write_arguments *arguments = (struct io_write_arguments *)_arguments;
|
|
700
720
|
|
|
701
|
-
const void *base;
|
|
702
|
-
size_t size;
|
|
703
|
-
rb_io_buffer_get_bytes_for_reading(arguments->buffer, &base, &size);
|
|
704
|
-
|
|
705
721
|
size_t length = arguments->length;
|
|
706
|
-
size_t offset = arguments->offset;
|
|
707
722
|
size_t total = 0;
|
|
708
723
|
|
|
709
|
-
|
|
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;
|
|
724
|
+
size_t maximum_size = arguments->size;
|
|
719
725
|
while (maximum_size) {
|
|
720
|
-
ssize_t result = write(arguments->descriptor, (char*)base+
|
|
726
|
+
ssize_t result = write(arguments->descriptor, (char*)arguments->base+total, maximum_size);
|
|
721
727
|
|
|
722
728
|
if (result > 0) {
|
|
723
729
|
total += result;
|
|
724
|
-
offset += result;
|
|
725
730
|
if ((size_t)result >= length) break;
|
|
731
|
+
maximum_size -= result;
|
|
726
732
|
length -= result;
|
|
727
733
|
} else if (result == 0) {
|
|
728
734
|
break;
|
|
@@ -731,8 +737,6 @@ VALUE io_write_loop(VALUE _arguments) {
|
|
|
731
737
|
} else {
|
|
732
738
|
return rb_fiber_scheduler_io_result(-1, errno);
|
|
733
739
|
}
|
|
734
|
-
|
|
735
|
-
maximum_size = size - offset;
|
|
736
740
|
}
|
|
737
741
|
|
|
738
742
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
@@ -748,11 +752,28 @@ VALUE io_write_ensure(VALUE _arguments) {
|
|
|
748
752
|
};
|
|
749
753
|
|
|
750
754
|
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);
|
|
752
|
-
|
|
753
755
|
size_t length = NUM2SIZET(_length);
|
|
754
756
|
size_t offset = NUM2SIZET(_offset);
|
|
755
757
|
|
|
758
|
+
const void *base;
|
|
759
|
+
size_t size;
|
|
760
|
+
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
761
|
+
|
|
762
|
+
if (length > size) {
|
|
763
|
+
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (offset > size) {
|
|
767
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
768
|
+
} else if (offset == size) {
|
|
769
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
base = (const char*)base + offset;
|
|
773
|
+
size -= offset;
|
|
774
|
+
|
|
775
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
776
|
+
|
|
756
777
|
struct io_write_arguments io_write_arguments = {
|
|
757
778
|
.self = self,
|
|
758
779
|
.fiber = fiber,
|
|
@@ -760,9 +781,9 @@ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
760
781
|
|
|
761
782
|
.flags = IO_Event_Selector_nonblock_set(descriptor),
|
|
762
783
|
.descriptor = descriptor,
|
|
763
|
-
.
|
|
784
|
+
.base = base,
|
|
785
|
+
.size = size,
|
|
764
786
|
.length = length,
|
|
765
|
-
.offset = offset,
|
|
766
787
|
};
|
|
767
788
|
|
|
768
789
|
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
@@ -809,14 +830,33 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
|
|
|
809
830
|
}
|
|
810
831
|
|
|
811
832
|
static
|
|
812
|
-
int
|
|
833
|
+
int timeout_is_nonblocking(struct timespec * timespec) {
|
|
813
834
|
return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
|
|
814
835
|
}
|
|
815
836
|
|
|
837
|
+
// Return true when it is safe and useful to enter the blocking selector wait.
|
|
838
|
+
static
|
|
839
|
+
int select_blocking_allowed(struct timespec * timespec) {
|
|
840
|
+
// 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.
|
|
841
|
+
if (timeout_is_nonblocking(timespec)) {
|
|
842
|
+
return 0;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
#ifndef RB_NOGVL_PENDING_INTERRUPT_FAIL
|
|
846
|
+
// On Rubies without `RB_NOGVL_PENDING_INTERRUPT_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
|
|
847
|
+
if (IO_Event_Selector_pending_interrupt()) {
|
|
848
|
+
return 0;
|
|
849
|
+
}
|
|
850
|
+
#endif
|
|
851
|
+
|
|
852
|
+
return 1;
|
|
853
|
+
}
|
|
854
|
+
|
|
816
855
|
struct select_arguments {
|
|
817
856
|
struct IO_Event_Selector_EPoll *selector;
|
|
818
857
|
|
|
819
858
|
int count;
|
|
859
|
+
int result;
|
|
820
860
|
struct epoll_event events[EPOLL_MAX_EVENTS];
|
|
821
861
|
|
|
822
862
|
struct timespec * timeout;
|
|
@@ -830,7 +870,7 @@ static int make_timeout_ms(struct timespec * timeout) {
|
|
|
830
870
|
return -1;
|
|
831
871
|
}
|
|
832
872
|
|
|
833
|
-
if (
|
|
873
|
+
if (timeout_is_nonblocking(timeout)) {
|
|
834
874
|
return 0;
|
|
835
875
|
}
|
|
836
876
|
|
|
@@ -851,13 +891,13 @@ void * select_internal(void *_arguments) {
|
|
|
851
891
|
struct select_arguments * arguments = (struct select_arguments *)_arguments;
|
|
852
892
|
|
|
853
893
|
#if defined(HAVE_EPOLL_PWAIT2)
|
|
854
|
-
arguments->
|
|
894
|
+
arguments->result = epoll_pwait2(arguments->selector->descriptor, arguments->events, arguments->count, arguments->timeout, NULL);
|
|
855
895
|
|
|
856
896
|
// Comment out the above line and enable the below lines to test ENOSYS code path.
|
|
857
|
-
// arguments->
|
|
897
|
+
// arguments->result = -1;
|
|
858
898
|
// errno = ENOSYS;
|
|
859
899
|
|
|
860
|
-
if (!enosys_error(arguments->
|
|
900
|
+
if (!enosys_error(arguments->result)) {
|
|
861
901
|
return NULL;
|
|
862
902
|
}
|
|
863
903
|
else {
|
|
@@ -865,37 +905,47 @@ void * select_internal(void *_arguments) {
|
|
|
865
905
|
}
|
|
866
906
|
#endif
|
|
867
907
|
|
|
868
|
-
arguments->
|
|
908
|
+
arguments->result = epoll_wait(arguments->selector->descriptor, arguments->events, arguments->count, make_timeout_ms(arguments->timeout));
|
|
869
909
|
|
|
870
910
|
return NULL;
|
|
871
911
|
}
|
|
872
912
|
|
|
873
913
|
static
|
|
874
|
-
|
|
914
|
+
int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
915
|
+
arguments->result = -1;
|
|
875
916
|
arguments->selector->blocked = 1;
|
|
876
|
-
|
|
917
|
+
#ifdef RB_NOGVL_PENDING_INTERRUPT_FAIL
|
|
918
|
+
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTERRUPT_FAIL);
|
|
919
|
+
#else
|
|
920
|
+
rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
921
|
+
#endif
|
|
877
922
|
arguments->selector->blocked = 0;
|
|
878
923
|
|
|
879
|
-
if (arguments->
|
|
880
|
-
|
|
924
|
+
if (arguments->result == -1) {
|
|
925
|
+
// If Ruby skips the native callback, the result sentinel can remain `-1`; `errno` may be `0` or `EINTR` depending on the Ruby implementation. Both cases mean the blocking wait did not produce any events.
|
|
926
|
+
if (errno != EINTR && errno != 0) {
|
|
881
927
|
rb_sys_fail("select_internal_without_gvl:epoll_wait");
|
|
882
928
|
} else {
|
|
883
|
-
|
|
929
|
+
return 0;
|
|
884
930
|
}
|
|
885
931
|
}
|
|
932
|
+
|
|
933
|
+
return arguments->result;
|
|
886
934
|
}
|
|
887
935
|
|
|
888
936
|
static
|
|
889
|
-
|
|
937
|
+
int select_internal_with_gvl(struct select_arguments *arguments) {
|
|
890
938
|
select_internal((void *)arguments);
|
|
891
939
|
|
|
892
|
-
if (arguments->
|
|
940
|
+
if (arguments->result == -1) {
|
|
893
941
|
if (errno != EINTR) {
|
|
894
942
|
rb_sys_fail("select_internal_with_gvl:epoll_wait");
|
|
895
943
|
} else {
|
|
896
|
-
|
|
944
|
+
return 0;
|
|
897
945
|
}
|
|
898
946
|
}
|
|
947
|
+
|
|
948
|
+
return arguments->result;
|
|
899
949
|
}
|
|
900
950
|
|
|
901
951
|
static
|
|
@@ -951,7 +1001,7 @@ VALUE select_handle_events(VALUE _arguments)
|
|
|
951
1001
|
struct select_arguments *arguments = (struct select_arguments *)_arguments;
|
|
952
1002
|
struct IO_Event_Selector_EPoll *selector = arguments->selector;
|
|
953
1003
|
|
|
954
|
-
for (int i = 0; i < arguments->
|
|
1004
|
+
for (int i = 0; i < arguments->result; i += 1) {
|
|
955
1005
|
const struct epoll_event *event = &arguments->events[i];
|
|
956
1006
|
if (DEBUG) fprintf(stderr, "-> fd=%d events=%d\n", event->data.fd, event->events);
|
|
957
1007
|
|
|
@@ -962,7 +1012,7 @@ VALUE select_handle_events(VALUE _arguments)
|
|
|
962
1012
|
}
|
|
963
1013
|
}
|
|
964
1014
|
|
|
965
|
-
return INT2NUM(arguments->
|
|
1015
|
+
return INT2NUM(arguments->result);
|
|
966
1016
|
}
|
|
967
1017
|
|
|
968
1018
|
static
|
|
@@ -987,6 +1037,8 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
|
|
|
987
1037
|
|
|
988
1038
|
struct select_arguments arguments = {
|
|
989
1039
|
.selector = selector,
|
|
1040
|
+
.count = EPOLL_MAX_EVENTS,
|
|
1041
|
+
.result = 0,
|
|
990
1042
|
.storage = {
|
|
991
1043
|
.tv_sec = 0,
|
|
992
1044
|
.tv_nsec = 0
|
|
@@ -997,22 +1049,22 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
|
|
|
997
1049
|
arguments.timeout = &arguments.storage;
|
|
998
1050
|
|
|
999
1051
|
// Process any currently pending events:
|
|
1000
|
-
select_internal_with_gvl(&arguments);
|
|
1052
|
+
int result = select_internal_with_gvl(&arguments);
|
|
1001
1053
|
|
|
1002
1054
|
// If we:
|
|
1003
1055
|
// 1. Didn't process any ready fibers, and
|
|
1004
1056
|
// 2. Didn't process any events from non-blocking select (above), and
|
|
1005
1057
|
// 3. There are no items in the ready list,
|
|
1006
1058
|
// then we can perform a blocking select.
|
|
1007
|
-
if (!ready && !
|
|
1059
|
+
if (!ready && !result && !selector->backend.ready) {
|
|
1008
1060
|
arguments.timeout = make_timeout(duration, &arguments.storage);
|
|
1009
1061
|
|
|
1010
|
-
if (
|
|
1062
|
+
if (select_blocking_allowed(arguments.timeout)) {
|
|
1011
1063
|
struct timespec start_time;
|
|
1012
1064
|
IO_Event_Time_current(&start_time);
|
|
1013
1065
|
|
|
1014
1066
|
// Wait for events to occur:
|
|
1015
|
-
select_internal_without_gvl(&arguments);
|
|
1067
|
+
result = select_internal_without_gvl(&arguments);
|
|
1016
1068
|
|
|
1017
1069
|
struct timespec end_time;
|
|
1018
1070
|
IO_Event_Time_current(&end_time);
|
|
@@ -1020,7 +1072,7 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
|
|
|
1020
1072
|
}
|
|
1021
1073
|
}
|
|
1022
1074
|
|
|
1023
|
-
if (
|
|
1075
|
+
if (result) {
|
|
1024
1076
|
return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
|
|
1025
1077
|
} else {
|
|
1026
1078
|
return RB_INT2NUM(0);
|
|
@@ -1079,6 +1131,7 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
|
|
|
1079
1131
|
rb_define_method(IO_Event_Selector_EPoll, "select", IO_Event_Selector_EPoll_select, 1);
|
|
1080
1132
|
rb_define_method(IO_Event_Selector_EPoll, "wakeup", IO_Event_Selector_EPoll_wakeup, 0);
|
|
1081
1133
|
rb_define_method(IO_Event_Selector_EPoll, "close", IO_Event_Selector_EPoll_close, 0);
|
|
1134
|
+
rb_define_method(IO_Event_Selector_EPoll, "closed?", IO_Event_Selector_EPoll_closed_p, 0);
|
|
1082
1135
|
|
|
1083
1136
|
rb_define_method(IO_Event_Selector_EPoll, "io_wait", IO_Event_Selector_EPoll_io_wait, 3);
|
|
1084
1137
|
|
|
@@ -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,6 +48,7 @@ struct IO_Event_Selector_KQueue
|
|
|
47
48
|
{
|
|
48
49
|
struct IO_Event_Selector backend;
|
|
49
50
|
int descriptor;
|
|
51
|
+
pid_t owner;
|
|
50
52
|
|
|
51
53
|
// Flag indicating whether the selector is currently blocked in a system call.
|
|
52
54
|
// Set to 1 when blocked in kevent() without GVL, 0 otherwise.
|
|
@@ -132,7 +134,14 @@ static
|
|
|
132
134
|
void close_internal(struct IO_Event_Selector_KQueue *selector)
|
|
133
135
|
{
|
|
134
136
|
if (selector->descriptor >= 0) {
|
|
135
|
-
|
|
137
|
+
if (selector->owner == getpid()) {
|
|
138
|
+
close(selector->descriptor);
|
|
139
|
+
|
|
140
|
+
#ifdef IO_EVENT_SELECTOR_KQUEUE_USE_INTERRUPT
|
|
141
|
+
IO_Event_Interrupt_close(&selector->interrupt);
|
|
142
|
+
#endif
|
|
143
|
+
}
|
|
144
|
+
|
|
136
145
|
selector->descriptor = -1;
|
|
137
146
|
}
|
|
138
147
|
}
|
|
@@ -289,6 +298,7 @@ VALUE IO_Event_Selector_KQueue_allocate(VALUE self) {
|
|
|
289
298
|
|
|
290
299
|
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
291
300
|
selector->descriptor = -1;
|
|
301
|
+
selector->owner = 0;
|
|
292
302
|
selector->blocked = 0;
|
|
293
303
|
|
|
294
304
|
selector->descriptors.element_initialize = IO_Event_Selector_KQueue_Descriptor_initialize;
|
|
@@ -331,6 +341,7 @@ VALUE IO_Event_Selector_KQueue_initialize(VALUE self, VALUE loop) {
|
|
|
331
341
|
ioctl(result, FIOCLEX);
|
|
332
342
|
|
|
333
343
|
selector->descriptor = result;
|
|
344
|
+
selector->owner = getpid();
|
|
334
345
|
|
|
335
346
|
rb_update_max_fd(selector->descriptor);
|
|
336
347
|
}
|
|
@@ -365,13 +376,16 @@ VALUE IO_Event_Selector_KQueue_close(VALUE self) {
|
|
|
365
376
|
|
|
366
377
|
close_internal(selector);
|
|
367
378
|
|
|
368
|
-
#ifdef IO_EVENT_SELECTOR_KQUEUE_USE_INTERRUPT
|
|
369
|
-
IO_Event_Interrupt_close(&selector->interrupt);
|
|
370
|
-
#endif
|
|
371
|
-
|
|
372
379
|
return Qnil;
|
|
373
380
|
}
|
|
374
381
|
|
|
382
|
+
VALUE IO_Event_Selector_KQueue_closed_p(VALUE self) {
|
|
383
|
+
struct IO_Event_Selector_KQueue *selector = NULL;
|
|
384
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
|
385
|
+
|
|
386
|
+
return selector->descriptor < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
|
|
387
|
+
}
|
|
388
|
+
|
|
375
389
|
VALUE IO_Event_Selector_KQueue_transfer(VALUE self)
|
|
376
390
|
{
|
|
377
391
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
|
@@ -578,40 +592,33 @@ struct io_read_arguments {
|
|
|
578
592
|
|
|
579
593
|
int descriptor;
|
|
580
594
|
|
|
581
|
-
|
|
595
|
+
// The remaining writable buffer region after applying the requested offset.
|
|
596
|
+
void *base;
|
|
597
|
+
size_t size;
|
|
598
|
+
|
|
599
|
+
// The minimum number of bytes requested by the caller.
|
|
582
600
|
size_t length;
|
|
583
|
-
size_t offset;
|
|
584
601
|
};
|
|
585
602
|
|
|
586
603
|
static
|
|
587
604
|
VALUE io_read_loop(VALUE _arguments) {
|
|
588
605
|
struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
|
|
589
606
|
|
|
590
|
-
void *base;
|
|
591
|
-
size_t size;
|
|
592
|
-
rb_io_buffer_get_bytes_for_writing(arguments->buffer, &base, &size);
|
|
593
|
-
|
|
594
607
|
size_t length = arguments->length;
|
|
595
|
-
size_t offset = arguments->offset;
|
|
596
608
|
size_t total = 0;
|
|
597
609
|
|
|
598
610
|
if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu)\n", arguments->descriptor, length);
|
|
599
611
|
|
|
600
|
-
|
|
601
|
-
if (offset > size) {
|
|
602
|
-
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
size_t maximum_size = size - offset;
|
|
612
|
+
size_t maximum_size = arguments->size;
|
|
606
613
|
while (maximum_size) {
|
|
607
|
-
if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld)\n", arguments->descriptor,
|
|
608
|
-
ssize_t result = read(arguments->descriptor, (char*)base+
|
|
609
|
-
if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld) -> %zd\n", arguments->descriptor,
|
|
614
|
+
if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld)\n", arguments->descriptor, total, maximum_size);
|
|
615
|
+
ssize_t result = read(arguments->descriptor, (char*)arguments->base+total, maximum_size);
|
|
616
|
+
if (DEBUG_IO_READ) fprintf(stderr, "read(%d, +%ld, %ld) -> %zd\n", arguments->descriptor, total, maximum_size, result);
|
|
610
617
|
|
|
611
618
|
if (result > 0) {
|
|
612
619
|
total += result;
|
|
613
|
-
offset += result;
|
|
614
620
|
if ((size_t)result >= length) break;
|
|
621
|
+
maximum_size -= result;
|
|
615
622
|
length -= result;
|
|
616
623
|
} else if (result == 0) {
|
|
617
624
|
break;
|
|
@@ -622,11 +629,9 @@ VALUE io_read_loop(VALUE _arguments) {
|
|
|
622
629
|
if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> errno=%d\n", arguments->descriptor, length, errno);
|
|
623
630
|
return rb_fiber_scheduler_io_result(-1, errno);
|
|
624
631
|
}
|
|
625
|
-
|
|
626
|
-
maximum_size = size - offset;
|
|
627
632
|
}
|
|
628
633
|
|
|
629
|
-
if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length,
|
|
634
|
+
if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, total);
|
|
630
635
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
631
636
|
}
|
|
632
637
|
|
|
@@ -643,11 +648,24 @@ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
643
648
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
|
644
649
|
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
|
645
650
|
|
|
646
|
-
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
647
|
-
|
|
648
651
|
size_t length = NUM2SIZET(_length);
|
|
649
652
|
size_t offset = NUM2SIZET(_offset);
|
|
650
653
|
|
|
654
|
+
void *base;
|
|
655
|
+
size_t size;
|
|
656
|
+
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
657
|
+
|
|
658
|
+
if (offset > size) {
|
|
659
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
660
|
+
} else if (offset == size) {
|
|
661
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
base = (char*)base + offset;
|
|
665
|
+
size -= offset;
|
|
666
|
+
|
|
667
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
668
|
+
|
|
651
669
|
struct io_read_arguments io_read_arguments = {
|
|
652
670
|
.self = self,
|
|
653
671
|
.fiber = fiber,
|
|
@@ -655,9 +673,9 @@ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
655
673
|
|
|
656
674
|
.flags = IO_Event_Selector_nonblock_set(descriptor),
|
|
657
675
|
.descriptor = descriptor,
|
|
658
|
-
.
|
|
676
|
+
.base = base,
|
|
677
|
+
.size = size,
|
|
659
678
|
.length = length,
|
|
660
|
-
.offset = offset,
|
|
661
679
|
};
|
|
662
680
|
|
|
663
681
|
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
@@ -687,44 +705,33 @@ struct io_write_arguments {
|
|
|
687
705
|
|
|
688
706
|
int descriptor;
|
|
689
707
|
|
|
690
|
-
|
|
708
|
+
// The remaining readable buffer region after applying the requested offset.
|
|
709
|
+
const void *base;
|
|
710
|
+
size_t size;
|
|
711
|
+
|
|
712
|
+
// The minimum number of bytes requested by the caller.
|
|
691
713
|
size_t length;
|
|
692
|
-
size_t offset;
|
|
693
714
|
};
|
|
694
715
|
|
|
695
716
|
static
|
|
696
717
|
VALUE io_write_loop(VALUE _arguments) {
|
|
697
718
|
struct io_write_arguments *arguments = (struct io_write_arguments *)_arguments;
|
|
698
719
|
|
|
699
|
-
const void *base;
|
|
700
|
-
size_t size;
|
|
701
|
-
rb_io_buffer_get_bytes_for_reading(arguments->buffer, &base, &size);
|
|
702
|
-
|
|
703
720
|
size_t length = arguments->length;
|
|
704
|
-
size_t offset = arguments->offset;
|
|
705
721
|
size_t total = 0;
|
|
706
722
|
|
|
707
|
-
if (length > size) {
|
|
708
|
-
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
709
|
-
}
|
|
710
|
-
|
|
711
723
|
if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu)\n", arguments->descriptor, length);
|
|
712
724
|
|
|
713
|
-
|
|
714
|
-
if (offset > size) {
|
|
715
|
-
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
size_t maximum_size = size - offset;
|
|
725
|
+
size_t maximum_size = arguments->size;
|
|
719
726
|
while (maximum_size) {
|
|
720
|
-
if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld, length=%zu)\n", arguments->descriptor,
|
|
721
|
-
ssize_t result = write(arguments->descriptor, (char*)base+
|
|
722
|
-
if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld) -> %zd\n", arguments->descriptor,
|
|
727
|
+
if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld, length=%zu)\n", arguments->descriptor, total, maximum_size, length);
|
|
728
|
+
ssize_t result = write(arguments->descriptor, (char*)arguments->base+total, maximum_size);
|
|
729
|
+
if (DEBUG_IO_WRITE) fprintf(stderr, "write(%d, +%ld, %ld) -> %zd\n", arguments->descriptor, total, maximum_size, result);
|
|
723
730
|
|
|
724
731
|
if (result > 0) {
|
|
725
732
|
total += result;
|
|
726
|
-
offset += result;
|
|
727
733
|
if ((size_t)result >= length) break;
|
|
734
|
+
maximum_size -= result;
|
|
728
735
|
length -= result;
|
|
729
736
|
} else if (result == 0) {
|
|
730
737
|
break;
|
|
@@ -735,11 +742,9 @@ VALUE io_write_loop(VALUE _arguments) {
|
|
|
735
742
|
if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> errno=%d\n", arguments->descriptor, length, errno);
|
|
736
743
|
return rb_fiber_scheduler_io_result(-1, errno);
|
|
737
744
|
}
|
|
738
|
-
|
|
739
|
-
maximum_size = size - offset;
|
|
740
745
|
}
|
|
741
746
|
|
|
742
|
-
if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length,
|
|
747
|
+
if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, total);
|
|
743
748
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
744
749
|
};
|
|
745
750
|
|
|
@@ -756,11 +761,28 @@ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
756
761
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
|
757
762
|
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
|
758
763
|
|
|
759
|
-
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
760
|
-
|
|
761
764
|
size_t length = NUM2SIZET(_length);
|
|
762
765
|
size_t offset = NUM2SIZET(_offset);
|
|
763
766
|
|
|
767
|
+
const void *base;
|
|
768
|
+
size_t size;
|
|
769
|
+
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
770
|
+
|
|
771
|
+
if (length > size) {
|
|
772
|
+
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (offset > size) {
|
|
776
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
777
|
+
} else if (offset == size) {
|
|
778
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
base = (const char*)base + offset;
|
|
782
|
+
size -= offset;
|
|
783
|
+
|
|
784
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
785
|
+
|
|
764
786
|
struct io_write_arguments io_write_arguments = {
|
|
765
787
|
.self = self,
|
|
766
788
|
.fiber = fiber,
|
|
@@ -768,9 +790,9 @@ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
768
790
|
|
|
769
791
|
.flags = IO_Event_Selector_nonblock_set(descriptor),
|
|
770
792
|
.descriptor = descriptor,
|
|
771
|
-
.
|
|
793
|
+
.base = base,
|
|
794
|
+
.size = size,
|
|
772
795
|
.length = length,
|
|
773
|
-
.offset = offset,
|
|
774
796
|
};
|
|
775
797
|
|
|
776
798
|
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
@@ -816,15 +838,29 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
|
|
|
816
838
|
return storage;
|
|
817
839
|
}
|
|
818
840
|
|
|
841
|
+
// Return true when it is safe and useful to enter the blocking selector wait.
|
|
819
842
|
static
|
|
820
|
-
int
|
|
821
|
-
|
|
843
|
+
int select_blocking_allowed(struct timespec * timespec) {
|
|
844
|
+
// 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.
|
|
845
|
+
if (timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0) {
|
|
846
|
+
return 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
#ifndef RB_NOGVL_PENDING_INTERRUPT_FAIL
|
|
850
|
+
// On Rubies without `RB_NOGVL_PENDING_INTERRUPT_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
|
|
851
|
+
if (IO_Event_Selector_pending_interrupt()) {
|
|
852
|
+
return 0;
|
|
853
|
+
}
|
|
854
|
+
#endif
|
|
855
|
+
|
|
856
|
+
return 1;
|
|
822
857
|
}
|
|
823
858
|
|
|
824
859
|
struct select_arguments {
|
|
825
860
|
struct IO_Event_Selector_KQueue *selector;
|
|
826
861
|
|
|
827
862
|
int count;
|
|
863
|
+
int result;
|
|
828
864
|
struct kevent events[KQUEUE_MAX_EVENTS];
|
|
829
865
|
|
|
830
866
|
struct timespec storage;
|
|
@@ -837,38 +873,48 @@ static
|
|
|
837
873
|
void * select_internal(void *_arguments) {
|
|
838
874
|
struct select_arguments * arguments = (struct select_arguments *)_arguments;
|
|
839
875
|
|
|
840
|
-
arguments->
|
|
876
|
+
arguments->result = kevent(arguments->selector->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
|
|
841
877
|
|
|
842
878
|
return NULL;
|
|
843
879
|
}
|
|
844
880
|
|
|
845
881
|
static
|
|
846
|
-
|
|
882
|
+
int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
883
|
+
arguments->result = -1;
|
|
847
884
|
arguments->selector->blocked = 1;
|
|
848
885
|
|
|
849
|
-
|
|
886
|
+
#ifdef RB_NOGVL_PENDING_INTERRUPT_FAIL
|
|
887
|
+
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTERRUPT_FAIL);
|
|
888
|
+
#else
|
|
889
|
+
rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
890
|
+
#endif
|
|
850
891
|
arguments->selector->blocked = 0;
|
|
851
892
|
|
|
852
|
-
if (arguments->
|
|
853
|
-
|
|
893
|
+
if (arguments->result == -1) {
|
|
894
|
+
// If Ruby skips the native callback, the result sentinel can remain `-1`; `errno` may be `0` or `EINTR` depending on the Ruby implementation. Both cases mean the blocking wait did not produce any events.
|
|
895
|
+
if (errno != EINTR && errno != 0) {
|
|
854
896
|
rb_sys_fail("select_internal_without_gvl:kevent");
|
|
855
897
|
} else {
|
|
856
|
-
|
|
898
|
+
return 0;
|
|
857
899
|
}
|
|
858
900
|
}
|
|
901
|
+
|
|
902
|
+
return arguments->result;
|
|
859
903
|
}
|
|
860
904
|
|
|
861
905
|
static
|
|
862
|
-
|
|
906
|
+
int select_internal_with_gvl(struct select_arguments *arguments) {
|
|
863
907
|
select_internal((void *)arguments);
|
|
864
908
|
|
|
865
|
-
if (arguments->
|
|
909
|
+
if (arguments->result == -1) {
|
|
866
910
|
if (errno != EINTR) {
|
|
867
911
|
rb_sys_fail("select_internal_with_gvl:kevent");
|
|
868
912
|
} else {
|
|
869
|
-
|
|
913
|
+
return 0;
|
|
870
914
|
}
|
|
871
915
|
}
|
|
916
|
+
|
|
917
|
+
return arguments->result;
|
|
872
918
|
}
|
|
873
919
|
|
|
874
920
|
static
|
|
@@ -922,14 +968,14 @@ VALUE select_handle_events(VALUE _arguments)
|
|
|
922
968
|
struct select_arguments *arguments = (struct select_arguments *)_arguments;
|
|
923
969
|
struct IO_Event_Selector_KQueue *selector = arguments->selector;
|
|
924
970
|
|
|
925
|
-
for (int i = 0; i < arguments->
|
|
971
|
+
for (int i = 0; i < arguments->result; i += 1) {
|
|
926
972
|
if (arguments->events[i].udata) {
|
|
927
973
|
struct IO_Event_Selector_KQueue_Descriptor *kqueue_descriptor = arguments->events[i].udata;
|
|
928
974
|
kqueue_descriptor->ready_events |= events_from_kevent_filter(arguments->events[i].filter);
|
|
929
975
|
}
|
|
930
976
|
}
|
|
931
977
|
|
|
932
|
-
for (int i = 0; i < arguments->
|
|
978
|
+
for (int i = 0; i < arguments->result; i += 1) {
|
|
933
979
|
if (arguments->events[i].udata) {
|
|
934
980
|
struct IO_Event_Selector_KQueue_Descriptor *kqueue_descriptor = arguments->events[i].udata;
|
|
935
981
|
IO_Event_Selector_KQueue_handle(selector, arguments->events[i].ident, kqueue_descriptor, &arguments->saved);
|
|
@@ -940,7 +986,7 @@ VALUE select_handle_events(VALUE _arguments)
|
|
|
940
986
|
}
|
|
941
987
|
}
|
|
942
988
|
|
|
943
|
-
return RB_INT2NUM(arguments->
|
|
989
|
+
return RB_INT2NUM(arguments->result);
|
|
944
990
|
}
|
|
945
991
|
|
|
946
992
|
static
|
|
@@ -965,6 +1011,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
|
965
1011
|
struct select_arguments arguments = {
|
|
966
1012
|
.selector = selector,
|
|
967
1013
|
.count = KQUEUE_MAX_EVENTS,
|
|
1014
|
+
.result = 0,
|
|
968
1015
|
.storage = {
|
|
969
1016
|
.tv_sec = 0,
|
|
970
1017
|
.tv_nsec = 0
|
|
@@ -975,14 +1022,14 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
|
975
1022
|
arguments.timeout = &arguments.storage;
|
|
976
1023
|
|
|
977
1024
|
// We break this implementation into two parts.
|
|
978
|
-
// (1)
|
|
979
|
-
// (2) without gvl: kevent(..., timeout = 0) if
|
|
1025
|
+
// (1) result = kevent(..., timeout = 0)
|
|
1026
|
+
// (2) without gvl: kevent(..., timeout = 0) if result == 0 and timeout != 0
|
|
980
1027
|
// This allows us to avoid releasing and reacquiring the GVL.
|
|
981
1028
|
// Non-comprehensive testing shows this gives a 1.5x speedup.
|
|
982
1029
|
|
|
983
1030
|
// First do the syscall with no timeout to get any immediately available events:
|
|
984
1031
|
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);
|
|
1032
|
+
int result = select_internal_with_gvl(&arguments);
|
|
986
1033
|
if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl done\r\n");
|
|
987
1034
|
|
|
988
1035
|
// If we:
|
|
@@ -990,17 +1037,15 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
|
990
1037
|
// 2. Didn't process any events from non-blocking select (above), and
|
|
991
1038
|
// 3. There are no items in the ready list,
|
|
992
1039
|
// then we can perform a blocking select.
|
|
993
|
-
if (!ready && !
|
|
1040
|
+
if (!ready && !result && !selector->backend.ready) {
|
|
994
1041
|
arguments.timeout = make_timeout(duration, &arguments.storage);
|
|
995
1042
|
|
|
996
|
-
if (
|
|
997
|
-
arguments.count = KQUEUE_MAX_EVENTS;
|
|
998
|
-
|
|
1043
|
+
if (select_blocking_allowed(arguments.timeout)) {
|
|
999
1044
|
struct timespec start_time;
|
|
1000
1045
|
IO_Event_Time_current(&start_time);
|
|
1001
1046
|
|
|
1002
1047
|
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);
|
|
1048
|
+
result = select_internal_without_gvl(&arguments);
|
|
1004
1049
|
|
|
1005
1050
|
struct timespec end_time;
|
|
1006
1051
|
IO_Event_Time_current(&end_time);
|
|
@@ -1008,7 +1053,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
|
1008
1053
|
}
|
|
1009
1054
|
}
|
|
1010
1055
|
|
|
1011
|
-
if (
|
|
1056
|
+
if (result) {
|
|
1012
1057
|
return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
|
|
1013
1058
|
} else {
|
|
1014
1059
|
return RB_INT2NUM(0);
|
|
@@ -1090,6 +1135,7 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
|
|
|
1090
1135
|
rb_define_method(IO_Event_Selector_KQueue, "select", IO_Event_Selector_KQueue_select, 1);
|
|
1091
1136
|
rb_define_method(IO_Event_Selector_KQueue, "wakeup", IO_Event_Selector_KQueue_wakeup, 0);
|
|
1092
1137
|
rb_define_method(IO_Event_Selector_KQueue, "close", IO_Event_Selector_KQueue_close, 0);
|
|
1138
|
+
rb_define_method(IO_Event_Selector_KQueue, "closed?", IO_Event_Selector_KQueue_closed_p, 0);
|
|
1093
1139
|
|
|
1094
1140
|
rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
|
|
1095
1141
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
static const int DEBUG = 0;
|
|
10
10
|
|
|
11
|
+
ID IO_Event_Selector_pending_interrupt_p_id;
|
|
12
|
+
|
|
11
13
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
|
12
14
|
static ID id_fileno;
|
|
13
15
|
|
|
@@ -79,6 +81,8 @@ static VALUE IO_Event_Selector_nonblock(VALUE class, VALUE io)
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
84
|
+
IO_Event_Selector_pending_interrupt_p_id = rb_intern("pending_interrupt?");
|
|
85
|
+
|
|
82
86
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
|
83
87
|
id_fileno = rb_intern("fileno");
|
|
84
88
|
#endif
|
|
@@ -40,6 +40,12 @@ static inline int IO_Event_try_again(int error) {
|
|
|
40
40
|
return error == EAGAIN || error == EWOULDBLOCK;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
extern ID IO_Event_Selector_pending_interrupt_p_id;
|
|
44
|
+
|
|
45
|
+
static inline int IO_Event_Selector_pending_interrupt(void) {
|
|
46
|
+
return RTEST(rb_funcall(rb_cThread, IO_Event_Selector_pending_interrupt_p_id, 0));
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
#ifdef HAVE_RB_IO_DESCRIPTOR
|
|
44
50
|
#define IO_Event_Selector_io_descriptor(io) rb_io_descriptor(io)
|
|
45
51
|
#else
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <stdbool.h>
|
|
12
12
|
#include <stdint.h>
|
|
13
13
|
#include <time.h>
|
|
14
|
+
#include <unistd.h>
|
|
14
15
|
|
|
15
16
|
#include "../interrupt.h"
|
|
16
17
|
|
|
@@ -32,6 +33,7 @@ struct IO_Event_Selector_URing
|
|
|
32
33
|
{
|
|
33
34
|
struct IO_Event_Selector backend;
|
|
34
35
|
struct io_uring ring;
|
|
36
|
+
pid_t owner;
|
|
35
37
|
|
|
36
38
|
// Flag indicating whether the selector is currently blocked in a system call.
|
|
37
39
|
// Set to 1 when blocked in io_uring_wait_cqe_timeout() without GVL, 0 otherwise.
|
|
@@ -115,14 +117,20 @@ void IO_Event_Selector_URing_Type_compact(void *_selector)
|
|
|
115
117
|
static
|
|
116
118
|
void close_internal(struct IO_Event_Selector_URing *selector)
|
|
117
119
|
{
|
|
118
|
-
if (selector->
|
|
119
|
-
|
|
120
|
+
if (selector->owner == getpid()) {
|
|
121
|
+
if (selector->interrupt.descriptor >= 0) {
|
|
122
|
+
IO_Event_Interrupt_close(&selector->interrupt);
|
|
123
|
+
selector->interrupt.descriptor = -1;
|
|
124
|
+
selector->wakeup_registered = 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (selector->ring.ring_fd >= 0) {
|
|
128
|
+
io_uring_queue_exit(&selector->ring);
|
|
129
|
+
selector->ring.ring_fd = -1;
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
120
132
|
selector->interrupt.descriptor = -1;
|
|
121
133
|
selector->wakeup_registered = 0;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (selector->ring.ring_fd >= 0) {
|
|
125
|
-
io_uring_queue_exit(&selector->ring);
|
|
126
134
|
selector->ring.ring_fd = -1;
|
|
127
135
|
}
|
|
128
136
|
}
|
|
@@ -237,6 +245,7 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
|
237
245
|
|
|
238
246
|
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
239
247
|
selector->ring.ring_fd = -1;
|
|
248
|
+
selector->owner = 0;
|
|
240
249
|
|
|
241
250
|
selector->blocked = 0;
|
|
242
251
|
selector->interrupt.descriptor = -1;
|
|
@@ -299,6 +308,8 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
|
|
|
299
308
|
rb_syserr_fail(-result, "IO_Event_Selector_URing_initialize:io_uring_queue_init");
|
|
300
309
|
}
|
|
301
310
|
|
|
311
|
+
selector->owner = getpid();
|
|
312
|
+
|
|
302
313
|
rb_update_max_fd(selector->ring.ring_fd);
|
|
303
314
|
|
|
304
315
|
// Interrupt for cross-thread wakeup: another thread calls signal(); the owner
|
|
@@ -339,6 +350,13 @@ VALUE IO_Event_Selector_URing_close(VALUE self) {
|
|
|
339
350
|
return Qnil;
|
|
340
351
|
}
|
|
341
352
|
|
|
353
|
+
VALUE IO_Event_Selector_URing_closed_p(VALUE self) {
|
|
354
|
+
struct IO_Event_Selector_URing *selector = NULL;
|
|
355
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
356
|
+
|
|
357
|
+
return selector->ring.ring_fd < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
|
|
358
|
+
}
|
|
359
|
+
|
|
342
360
|
VALUE IO_Event_Selector_URing_transfer(VALUE self)
|
|
343
361
|
{
|
|
344
362
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
@@ -757,8 +775,6 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
757
775
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
758
776
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
759
777
|
|
|
760
|
-
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
761
|
-
|
|
762
778
|
void *base;
|
|
763
779
|
size_t size;
|
|
764
780
|
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
@@ -766,13 +782,17 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
766
782
|
size_t length = NUM2SIZET(_length);
|
|
767
783
|
size_t offset = NUM2SIZET(_offset);
|
|
768
784
|
size_t total = 0;
|
|
769
|
-
off_t from = io_seekable(descriptor);
|
|
770
785
|
|
|
771
786
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
772
787
|
if (offset > size) {
|
|
773
788
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
789
|
+
} else if (offset == size) {
|
|
790
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
774
791
|
}
|
|
775
792
|
|
|
793
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
794
|
+
off_t from = io_seekable(descriptor);
|
|
795
|
+
|
|
776
796
|
size_t maximum_size = size - offset;
|
|
777
797
|
|
|
778
798
|
// Are we performing a non-blocking read?
|
|
@@ -827,8 +847,6 @@ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
827
847
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
828
848
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
829
849
|
|
|
830
|
-
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
831
|
-
|
|
832
850
|
void *base;
|
|
833
851
|
size_t size;
|
|
834
852
|
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
@@ -841,8 +859,12 @@ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
841
859
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
842
860
|
if (offset > size) {
|
|
843
861
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
862
|
+
} else if (offset == size) {
|
|
863
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
844
864
|
}
|
|
845
865
|
|
|
866
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
867
|
+
|
|
846
868
|
size_t maximum_size = size - offset;
|
|
847
869
|
while (maximum_size) {
|
|
848
870
|
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -945,8 +967,6 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
945
967
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
946
968
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
947
969
|
|
|
948
|
-
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
949
|
-
|
|
950
970
|
const void *base;
|
|
951
971
|
size_t size;
|
|
952
972
|
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
@@ -954,7 +974,6 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
954
974
|
size_t length = NUM2SIZET(_length);
|
|
955
975
|
size_t offset = NUM2SIZET(_offset);
|
|
956
976
|
size_t total = 0;
|
|
957
|
-
off_t from = io_seekable(descriptor);
|
|
958
977
|
|
|
959
978
|
if (length > size) {
|
|
960
979
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
@@ -963,8 +982,13 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
963
982
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
964
983
|
if (offset > size) {
|
|
965
984
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
985
|
+
} else if (offset == size) {
|
|
986
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
966
987
|
}
|
|
967
|
-
|
|
988
|
+
|
|
989
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
990
|
+
off_t from = io_seekable(descriptor);
|
|
991
|
+
|
|
968
992
|
size_t maximum_size = size - offset;
|
|
969
993
|
while (maximum_size) {
|
|
970
994
|
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -1005,8 +1029,6 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
1005
1029
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
1006
1030
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
1007
1031
|
|
|
1008
|
-
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
1009
|
-
|
|
1010
1032
|
const void *base;
|
|
1011
1033
|
size_t size;
|
|
1012
1034
|
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
@@ -1023,8 +1045,12 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
1023
1045
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
1024
1046
|
if (offset > size) {
|
|
1025
1047
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1048
|
+
} else if (offset == size) {
|
|
1049
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
1026
1050
|
}
|
|
1027
|
-
|
|
1051
|
+
|
|
1052
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
1053
|
+
|
|
1028
1054
|
size_t maximum_size = size - offset;
|
|
1029
1055
|
while (maximum_size) {
|
|
1030
1056
|
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -1104,9 +1130,22 @@ struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec
|
|
|
1104
1130
|
return storage;
|
|
1105
1131
|
}
|
|
1106
1132
|
|
|
1133
|
+
// Return true when it is safe and useful to enter the blocking selector wait.
|
|
1107
1134
|
static
|
|
1108
|
-
int
|
|
1109
|
-
|
|
1135
|
+
int select_blocking_allowed(struct __kernel_timespec *timespec) {
|
|
1136
|
+
// 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.
|
|
1137
|
+
if (timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0) {
|
|
1138
|
+
return 0;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
#ifndef RB_NOGVL_PENDING_INTERRUPT_FAIL
|
|
1142
|
+
// On Rubies without `RB_NOGVL_PENDING_INTERRUPT_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
|
|
1143
|
+
if (IO_Event_Selector_pending_interrupt()) {
|
|
1144
|
+
return 0;
|
|
1145
|
+
}
|
|
1146
|
+
#endif
|
|
1147
|
+
|
|
1148
|
+
return 1;
|
|
1110
1149
|
}
|
|
1111
1150
|
|
|
1112
1151
|
struct select_arguments {
|
|
@@ -1145,22 +1184,27 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
|
1145
1184
|
|
|
1146
1185
|
io_uring_submit_flush(selector);
|
|
1147
1186
|
|
|
1187
|
+
arguments->result = -EINTR;
|
|
1148
1188
|
selector->blocked = 1;
|
|
1149
|
-
|
|
1189
|
+
#ifdef RB_NOGVL_PENDING_INTERRUPT_FAIL
|
|
1190
|
+
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTERRUPT_FAIL);
|
|
1191
|
+
#else
|
|
1192
|
+
rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
1193
|
+
#endif
|
|
1150
1194
|
selector->blocked = 0;
|
|
1151
1195
|
|
|
1152
1196
|
if (arguments->result == -ETIME) {
|
|
1153
|
-
|
|
1197
|
+
return 0;
|
|
1154
1198
|
} else if (arguments->result == -EINTR) {
|
|
1155
|
-
|
|
1199
|
+
return 0;
|
|
1156
1200
|
} else if (arguments->result < 0) {
|
|
1157
1201
|
rb_syserr_fail(-arguments->result, "select_internal_without_gvl:io_uring_wait_cqe_timeout");
|
|
1158
1202
|
} else {
|
|
1159
1203
|
// At least 1 event is waiting:
|
|
1160
|
-
|
|
1204
|
+
return 1;
|
|
1161
1205
|
}
|
|
1162
1206
|
|
|
1163
|
-
return
|
|
1207
|
+
return 0;
|
|
1164
1208
|
}
|
|
1165
1209
|
|
|
1166
1210
|
static inline
|
|
@@ -1257,28 +1301,29 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
|
1257
1301
|
|
|
1258
1302
|
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
|
1259
1303
|
|
|
1260
|
-
int
|
|
1304
|
+
int completed = select_process_completions(selector);
|
|
1261
1305
|
|
|
1262
1306
|
// If we:
|
|
1263
1307
|
// 1. Didn't process any ready fibers, and
|
|
1264
1308
|
// 2. Didn't process any events from non-blocking select (above), and
|
|
1265
1309
|
// 3. There are no items in the ready list,
|
|
1266
1310
|
// then we can perform a blocking select.
|
|
1267
|
-
if (!ready && !
|
|
1311
|
+
if (!ready && !completed && !selector->backend.ready) {
|
|
1268
1312
|
// We might need to wait for events:
|
|
1269
1313
|
struct select_arguments arguments = {
|
|
1270
1314
|
.selector = selector,
|
|
1315
|
+
.result = 0,
|
|
1271
1316
|
.timeout = NULL,
|
|
1272
1317
|
};
|
|
1273
1318
|
|
|
1274
1319
|
arguments.timeout = make_timeout(duration, &arguments.storage);
|
|
1275
1320
|
|
|
1276
|
-
if (!selector->backend.ready &&
|
|
1321
|
+
if (!selector->backend.ready && select_blocking_allowed(arguments.timeout)) {
|
|
1277
1322
|
struct timespec start_time;
|
|
1278
1323
|
IO_Event_Time_current(&start_time);
|
|
1279
1324
|
|
|
1280
1325
|
// This is a blocking operation, we wait for events:
|
|
1281
|
-
result = select_internal_without_gvl(&arguments);
|
|
1326
|
+
int result = select_internal_without_gvl(&arguments);
|
|
1282
1327
|
|
|
1283
1328
|
struct timespec end_time;
|
|
1284
1329
|
IO_Event_Time_current(&end_time);
|
|
@@ -1286,12 +1331,12 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
|
1286
1331
|
|
|
1287
1332
|
// After waiting/flushing the SQ, check if there are any completions:
|
|
1288
1333
|
if (result > 0) {
|
|
1289
|
-
|
|
1334
|
+
completed = select_process_completions(selector);
|
|
1290
1335
|
}
|
|
1291
1336
|
}
|
|
1292
1337
|
}
|
|
1293
1338
|
|
|
1294
|
-
return RB_INT2NUM(
|
|
1339
|
+
return RB_INT2NUM(completed);
|
|
1295
1340
|
}
|
|
1296
1341
|
|
|
1297
1342
|
VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
|
@@ -1370,6 +1415,7 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
|
1370
1415
|
rb_define_method(IO_Event_Selector_URing, "select", IO_Event_Selector_URing_select, 1);
|
|
1371
1416
|
rb_define_method(IO_Event_Selector_URing, "wakeup", IO_Event_Selector_URing_wakeup, 0);
|
|
1372
1417
|
rb_define_method(IO_Event_Selector_URing, "close", IO_Event_Selector_URing_close, 0);
|
|
1418
|
+
rb_define_method(IO_Event_Selector_URing, "closed?", IO_Event_Selector_URing_closed_p, 0);
|
|
1373
1419
|
|
|
1374
1420
|
rb_define_method(IO_Event_Selector_URing, "io_wait", IO_Event_Selector_URing_io_wait, 3);
|
|
1375
1421
|
|
|
@@ -111,6 +111,11 @@ module IO::Event
|
|
|
111
111
|
@log&.flush
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# @returns [Boolean] Whether the wrapped selector is closed.
|
|
115
|
+
def closed?
|
|
116
|
+
@selector.nil? || @selector.closed?
|
|
117
|
+
end
|
|
118
|
+
|
|
114
119
|
# Transfer from the calling fiber to the selector.
|
|
115
120
|
def transfer
|
|
116
121
|
log("Transfering to event loop")
|
|
@@ -13,6 +13,7 @@ module IO::Event
|
|
|
13
13
|
# Initialize the selector with the given event loop fiber.
|
|
14
14
|
def initialize(loop)
|
|
15
15
|
@loop = loop
|
|
16
|
+
@owner = Process.pid
|
|
16
17
|
|
|
17
18
|
@waiting = Hash.new.compare_by_identity
|
|
18
19
|
|
|
@@ -52,6 +53,11 @@ module IO::Event
|
|
|
52
53
|
@waiting = nil
|
|
53
54
|
end
|
|
54
55
|
|
|
56
|
+
# @returns [Boolean] Whether the selector is closed or belongs to a different process.
|
|
57
|
+
def closed?
|
|
58
|
+
@loop.nil? || @owner != Process.pid
|
|
59
|
+
end
|
|
60
|
+
|
|
55
61
|
# An optional reference to a fiber which can be cleared before it is resumed.
|
|
56
62
|
Optional = Struct.new(:fiber) do
|
|
57
63
|
# Transfer control to the fiber if it is still available.
|
|
@@ -199,6 +205,8 @@ module IO::Event
|
|
|
199
205
|
# Ensure offset is within the bounds of the buffer to avoid ArgumentError
|
|
200
206
|
if offset > buffer.size
|
|
201
207
|
return -Errno::EINVAL::Errno
|
|
208
|
+
elsif offset == buffer.size
|
|
209
|
+
return 0
|
|
202
210
|
end
|
|
203
211
|
|
|
204
212
|
total = 0
|
|
@@ -234,6 +242,8 @@ module IO::Event
|
|
|
234
242
|
# Ensure offset is within the bounds of the buffer to avoid ArgumentError
|
|
235
243
|
if offset > buffer.size
|
|
236
244
|
return -Errno::EINVAL::Errno
|
|
245
|
+
elsif offset == buffer.size
|
|
246
|
+
return 0
|
|
237
247
|
end
|
|
238
248
|
|
|
239
249
|
total = 0
|
data/lib/io/event/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -18,6 +18,14 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
|
|
|
18
18
|
|
|
19
19
|
Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
|
|
20
20
|
|
|
21
|
+
### v1.18.0
|
|
22
|
+
|
|
23
|
+
- **Fixed**: Avoid entering a blocking native selector wait when an interrupt is already pending for the current thread.
|
|
24
|
+
|
|
25
|
+
### v1.17.0
|
|
26
|
+
|
|
27
|
+
- Report inherited selector objects as closed after fork, and avoid closing descriptors they no longer own.
|
|
28
|
+
|
|
21
29
|
### v1.16.4
|
|
22
30
|
|
|
23
31
|
- Correctly implement `Interrupt#signal` so that it is robust enough to be called by `Scheduler#unblock`.
|
|
@@ -56,14 +64,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
|
|
|
56
64
|
|
|
57
65
|
- Allow `epoll_pwait2` to be disabled via `--disable-epoll_pwait2`.
|
|
58
66
|
|
|
59
|
-
### v1.14.3
|
|
60
|
-
|
|
61
|
-
- Fix several implementation bugs that could cause deadlocks on blocking writes.
|
|
62
|
-
|
|
63
|
-
### v1.14.0
|
|
64
|
-
|
|
65
|
-
- [Enhanced `IO::Event::PriorityHeap` with deletion and bulk insertion methods](https://socketry.github.io/io-event/releases/index#enhanced-io::event::priorityheap-with-deletion-and-bulk-insertion-methods)
|
|
66
|
-
|
|
67
67
|
## Contributing
|
|
68
68
|
|
|
69
69
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v1.18.0
|
|
4
|
+
|
|
5
|
+
- **Fixed**: Avoid entering a blocking native selector wait when an interrupt is already pending for the current thread.
|
|
6
|
+
|
|
7
|
+
## v1.17.0
|
|
8
|
+
|
|
9
|
+
- Report inherited selector objects as closed after fork, and avoid closing descriptors they no longer own.
|
|
10
|
+
|
|
3
11
|
## v1.16.4
|
|
4
12
|
|
|
5
13
|
- Correctly implement `Interrupt#signal` so that it is robust enough to be called by `Scheduler#unblock`.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|