io-event 1.16.4 → 1.17.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 +71 -49
- data/ext/io/event/selector/kqueue.c +80 -57
- data/ext/io/event/selector/uring.c +45 -18
- 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 +4 -4
- data/releases.md +4 -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: be6f12aa408dcf76d86fbac8ae598d4a6313b65275357d611eeae9dc2fd1fe75
|
|
4
|
+
data.tar.gz: facad52118ca469a78fa08187f14265fc98b577b1e703d76e66c83fc733a3b21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a32f4bf15f0377d88912e799f87898787ee817e24c2b3638dca609d54ebc9ef3c97258732545be36fb90ef0a26455c210047872f686736d5fae2d6fbf8495aef
|
|
7
|
+
data.tar.gz: 931c9b8ee443cc311a8d8823dde3128f109e8cdc739fdc421bf959c5ebfb8fea9a17a17fdc2020a65b0b970eafa91d4cba7fa6ff483165b2c4fca862061ca93c
|
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);
|
|
@@ -1079,6 +1100,7 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
|
|
|
1079
1100
|
rb_define_method(IO_Event_Selector_EPoll, "select", IO_Event_Selector_EPoll_select, 1);
|
|
1080
1101
|
rb_define_method(IO_Event_Selector_EPoll, "wakeup", IO_Event_Selector_EPoll_wakeup, 0);
|
|
1081
1102
|
rb_define_method(IO_Event_Selector_EPoll, "close", IO_Event_Selector_EPoll_close, 0);
|
|
1103
|
+
rb_define_method(IO_Event_Selector_EPoll, "closed?", IO_Event_Selector_EPoll_closed_p, 0);
|
|
1082
1104
|
|
|
1083
1105
|
rb_define_method(IO_Event_Selector_EPoll, "io_wait", IO_Event_Selector_EPoll_io_wait, 3);
|
|
1084
1106
|
|
|
@@ -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);
|
|
@@ -1090,6 +1112,7 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
|
|
|
1090
1112
|
rb_define_method(IO_Event_Selector_KQueue, "select", IO_Event_Selector_KQueue_select, 1);
|
|
1091
1113
|
rb_define_method(IO_Event_Selector_KQueue, "wakeup", IO_Event_Selector_KQueue_wakeup, 0);
|
|
1092
1114
|
rb_define_method(IO_Event_Selector_KQueue, "close", IO_Event_Selector_KQueue_close, 0);
|
|
1115
|
+
rb_define_method(IO_Event_Selector_KQueue, "closed?", IO_Event_Selector_KQueue_closed_p, 0);
|
|
1093
1116
|
|
|
1094
1117
|
rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
|
|
1095
1118
|
|
|
@@ -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);
|
|
@@ -1370,6 +1396,7 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
|
1370
1396
|
rb_define_method(IO_Event_Selector_URing, "select", IO_Event_Selector_URing_select, 1);
|
|
1371
1397
|
rb_define_method(IO_Event_Selector_URing, "wakeup", IO_Event_Selector_URing_wakeup, 0);
|
|
1372
1398
|
rb_define_method(IO_Event_Selector_URing, "close", IO_Event_Selector_URing_close, 0);
|
|
1399
|
+
rb_define_method(IO_Event_Selector_URing, "closed?", IO_Event_Selector_URing_closed_p, 0);
|
|
1373
1400
|
|
|
1374
1401
|
rb_define_method(IO_Event_Selector_URing, "io_wait", IO_Event_Selector_URing_io_wait, 3);
|
|
1375
1402
|
|
|
@@ -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,10 @@ 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.17.0
|
|
22
|
+
|
|
23
|
+
- Report inherited selector objects as closed after fork, and avoid closing descriptors they no longer own.
|
|
24
|
+
|
|
21
25
|
### v1.16.4
|
|
22
26
|
|
|
23
27
|
- Correctly implement `Interrupt#signal` so that it is robust enough to be called by `Scheduler#unblock`.
|
|
@@ -60,10 +64,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
|
|
|
60
64
|
|
|
61
65
|
- Fix several implementation bugs that could cause deadlocks on blocking writes.
|
|
62
66
|
|
|
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
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|