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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +31 -5
- data/ext/extconf.rb +4 -4
- data/ext/io/event/list.h +1 -1
- data/ext/io/event/selector/epoll.c +196 -97
- data/ext/io/event/selector/kqueue.c +195 -101
- data/ext/io/event/selector/selector.c +77 -1
- data/ext/io/event/selector/selector.h +24 -2
- data/ext/io/event/selector/uring.c +508 -88
- data/ext/io/event/worker_pool.c +45 -34
- data/lib/io/event/debug/selector.rb +13 -6
- data/lib/io/event/interrupt.rb +12 -7
- data/lib/io/event/selector/select.rb +108 -58
- data/lib/io/event/selector.rb +24 -9
- data/lib/io/event/timers.rb +1 -1
- data/lib/io/event/version.rb +1 -1
- data/license.md +1 -0
- data/readme.md +22 -26
- data/releases.md +50 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
|
@@ -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
|
-
|
|
135
|
-
|
|
131
|
+
if (selector->owner == getpid()) {
|
|
132
|
+
close(selector->descriptor);
|
|
133
|
+
IO_Event_Interrupt_close(&selector->interrupt);
|
|
134
|
+
}
|
|
136
135
|
|
|
137
|
-
|
|
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->
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
|
|
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+
|
|
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
|
|
648
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
700
|
+
.base = base,
|
|
701
|
+
.size = size,
|
|
702
|
+
#if RUBY_FIBER_SCHEDULER_VERSION < 4
|
|
661
703
|
.length = length,
|
|
662
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
702
|
-
|
|
703
|
-
|
|
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
|
-
|
|
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+
|
|
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
|
|
751
|
-
|
|
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
|
-
|
|
754
|
-
|
|
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
|
-
.
|
|
836
|
+
.base = base,
|
|
837
|
+
.size = size,
|
|
838
|
+
#if RUBY_FIBER_SCHEDULER_VERSION < 4
|
|
764
839
|
.length = length,
|
|
765
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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->
|
|
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->
|
|
858
|
-
//
|
|
947
|
+
// arguments->result = -1;
|
|
948
|
+
// arguments->error = ENOSYS;
|
|
859
949
|
|
|
860
|
-
if (!enosys_error(arguments->
|
|
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->
|
|
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
|
-
|
|
875
|
-
arguments->
|
|
876
|
-
|
|
877
|
-
arguments->selector->
|
|
878
|
-
|
|
879
|
-
if (arguments->
|
|
880
|
-
if (
|
|
881
|
-
|
|
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
|
-
|
|
974
|
+
return 0;
|
|
884
975
|
}
|
|
885
976
|
}
|
|
977
|
+
|
|
978
|
+
return arguments->result;
|
|
886
979
|
}
|
|
887
980
|
|
|
888
981
|
static
|
|
889
|
-
|
|
982
|
+
int select_internal_with_gvl(struct select_arguments *arguments) {
|
|
890
983
|
select_internal((void *)arguments);
|
|
891
984
|
|
|
892
|
-
if (arguments->
|
|
893
|
-
if (
|
|
894
|
-
|
|
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
|
-
|
|
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->
|
|
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->
|
|
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 && !
|
|
1104
|
+
if (!ready && !result && !selector->backend.ready) {
|
|
1008
1105
|
arguments.timeout = make_timeout(duration, &arguments.storage);
|
|
1009
1106
|
|
|
1010
|
-
if (
|
|
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 (
|
|
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
|
}
|