io-event 0.1.0 → 0.2.3
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
- data/ext/IO_Event.bundle +0 -0
- data/ext/Makefile +2 -2
- data/ext/extconf.rb +3 -0
- data/ext/interrupt.o +0 -0
- data/ext/io/event/interrupt.c +108 -0
- data/ext/io/event/interrupt.h +47 -0
- data/ext/io/event/selector/epoll.c +70 -8
- data/ext/io/event/selector/epoll.h +2 -0
- data/ext/io/event/selector/kqueue.c +53 -7
- data/ext/io/event/selector/selector.h +2 -0
- data/ext/io/event/selector/uring.c +65 -13
- data/ext/io/event/selector/uring.h +0 -1
- data/ext/kqueue.o +0 -0
- data/ext/mkmf.log +17 -1
- data/lib/io/event/debug/selector.rb +15 -3
- data/lib/io/event/interrupt.rb +57 -0
- data/lib/io/event/selector/select.rb +37 -11
- data/lib/io/event/selector.rb +0 -5
- data/lib/io/event/version.rb +1 -1
- metadata +9 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0978ed0fd5d85399283cd8c4bb296f44662af2fad9a67e992000f3214898f5a
|
4
|
+
data.tar.gz: 154503dd7f0f52dc2ad166d262bb371f81fc60e148dd0c74eadb65051bc5e71f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a54cab01e6c314890284d9640e3404f6a34706402b2cbf6383bff69c6c341c0c84c56f64d1feebb46e875539e1baab95749ce7f41e5f15ae13af0d8b9e1e0c1a
|
7
|
+
data.tar.gz: 7b2891a70e1703616f104410d4f86898a5d9c2bfa6c8ecde0295bea1bfed6ee7efada50568bdf8f841e0360db370d209259f66f2cb17f97d2be47db8a1c2e101
|
data/ext/IO_Event.bundle
CHANGED
Binary file
|
data/ext/Makefile
CHANGED
@@ -139,8 +139,8 @@ target_prefix =
|
|
139
139
|
LOCAL_LIBS =
|
140
140
|
LIBS =
|
141
141
|
ORIG_SRCS =
|
142
|
-
SRCS = $(ORIG_SRCS) event.c selector.c kqueue.c
|
143
|
-
OBJS = event.o selector.o kqueue.o
|
142
|
+
SRCS = $(ORIG_SRCS) event.c selector.c kqueue.c interrupt.c
|
143
|
+
OBJS = event.o selector.o kqueue.o interrupt.o
|
144
144
|
HDRS = $(srcdir)/extconf.h
|
145
145
|
LOCAL_HDRS =
|
146
146
|
TARGET = IO_Event
|
data/ext/extconf.rb
CHANGED
data/ext/interrupt.o
ADDED
Binary file
|
@@ -0,0 +1,108 @@
|
|
1
|
+
// Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
//
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
8
|
+
// furnished to do so, subject to the following conditions:
|
9
|
+
//
|
10
|
+
// The above copyright notice and this permission notice shall be included in
|
11
|
+
// all copies or substantial portions of the Software.
|
12
|
+
//
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
// THE SOFTWARE.
|
20
|
+
|
21
|
+
// static const int DEBUG = 0;
|
22
|
+
|
23
|
+
#include "interrupt.h"
|
24
|
+
#include <unistd.h>
|
25
|
+
|
26
|
+
#include "selector/selector.h"
|
27
|
+
|
28
|
+
#ifdef HAVE_SYS_EVENTFD_H
|
29
|
+
#include <sys/eventfd.h>
|
30
|
+
|
31
|
+
void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt)
|
32
|
+
{
|
33
|
+
interrupt->descriptor = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
34
|
+
rb_update_max_fd(interrupt->descriptor);
|
35
|
+
}
|
36
|
+
|
37
|
+
void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt)
|
38
|
+
{
|
39
|
+
close(interrupt->descriptor);
|
40
|
+
}
|
41
|
+
|
42
|
+
void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
|
43
|
+
{
|
44
|
+
uint64_t value = 1;
|
45
|
+
ssize_t result = write(interrupt->descriptor, &value, sizeof(value));
|
46
|
+
|
47
|
+
if (result == -1) {
|
48
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) return;
|
49
|
+
|
50
|
+
rb_sys_fail("IO_Event_Interrupt_signal:write");
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
|
55
|
+
{
|
56
|
+
uint64_t value = 0;
|
57
|
+
ssize_t result = read(interrupt->descriptor, &value, sizeof(value));
|
58
|
+
|
59
|
+
if (result == -1) {
|
60
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) return;
|
61
|
+
|
62
|
+
rb_sys_fail("IO_Event_Interrupt_clear:read");
|
63
|
+
}
|
64
|
+
}
|
65
|
+
#else
|
66
|
+
void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt)
|
67
|
+
{
|
68
|
+
#ifdef __linux__
|
69
|
+
pipe2(interrupt->descriptor, O_CLOEXEC | O_NONBLOCK);
|
70
|
+
#else
|
71
|
+
pipe(interrupt->descriptor);
|
72
|
+
IO_Event_Selector_nonblock_set(interrupt->descriptor[0]);
|
73
|
+
IO_Event_Selector_nonblock_set(interrupt->descriptor[1]);
|
74
|
+
#endif
|
75
|
+
|
76
|
+
rb_update_max_fd(interrupt->descriptor[0]);
|
77
|
+
rb_update_max_fd(interrupt->descriptor[1]);
|
78
|
+
}
|
79
|
+
|
80
|
+
void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt)
|
81
|
+
{
|
82
|
+
close(interrupt->descriptor[0]);
|
83
|
+
close(interrupt->descriptor[1]);
|
84
|
+
}
|
85
|
+
|
86
|
+
void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
|
87
|
+
{
|
88
|
+
ssize_t result = write(interrupt->descriptor[1], ".", 1);
|
89
|
+
|
90
|
+
if (result == -1) {
|
91
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) return;
|
92
|
+
|
93
|
+
rb_sys_fail("IO_Event_Interrupt_signal:write");
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
|
98
|
+
{
|
99
|
+
char buffer[128];
|
100
|
+
ssize_t result = read(interrupt->descriptor[0], buffer, sizeof(buffer));
|
101
|
+
|
102
|
+
if (result == -1) {
|
103
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) return;
|
104
|
+
|
105
|
+
rb_sys_fail("IO_Event_Interrupt_clear:read");
|
106
|
+
}
|
107
|
+
}
|
108
|
+
#endif
|
@@ -0,0 +1,47 @@
|
|
1
|
+
// Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
//
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
8
|
+
// furnished to do so, subject to the following conditions:
|
9
|
+
//
|
10
|
+
// The above copyright notice and this permission notice shall be included in
|
11
|
+
// all copies or substantial portions of the Software.
|
12
|
+
//
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
// THE SOFTWARE.
|
20
|
+
|
21
|
+
#pragma once
|
22
|
+
|
23
|
+
#include "extconf.h"
|
24
|
+
|
25
|
+
#ifdef HAVE_SYS_EVENTFD_H
|
26
|
+
struct IO_Event_Interrupt {
|
27
|
+
int descriptor;
|
28
|
+
};
|
29
|
+
|
30
|
+
inline int IO_Event_Interrupt_descriptor(struct IO_Event_Interrupt *interrupt) {
|
31
|
+
return interrupt->descriptor;
|
32
|
+
}
|
33
|
+
#else
|
34
|
+
struct IO_Event_Interrupt {
|
35
|
+
int descriptor[2];
|
36
|
+
};
|
37
|
+
|
38
|
+
inline int IO_Event_Interrupt_descriptor(struct IO_Event_Interrupt *interrupt) {
|
39
|
+
return interrupt->descriptor[0];
|
40
|
+
}
|
41
|
+
#endif
|
42
|
+
|
43
|
+
void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt);
|
44
|
+
void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt);
|
45
|
+
|
46
|
+
void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt);
|
47
|
+
void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt);
|
@@ -26,6 +26,9 @@
|
|
26
26
|
#include <errno.h>
|
27
27
|
|
28
28
|
#include "pidfd.c"
|
29
|
+
#include "../interrupt.h"
|
30
|
+
|
31
|
+
static const int DEBUG = 0;
|
29
32
|
|
30
33
|
static VALUE IO_Event_Selector_EPoll = Qnil;
|
31
34
|
|
@@ -34,6 +37,8 @@ enum {EPOLL_MAX_EVENTS = 64};
|
|
34
37
|
struct IO_Event_Selector_EPoll {
|
35
38
|
struct IO_Event_Selector backend;
|
36
39
|
int descriptor;
|
40
|
+
int blocked;
|
41
|
+
struct IO_Event_Interrupt interrupt;
|
37
42
|
};
|
38
43
|
|
39
44
|
void IO_Event_Selector_EPoll_Type_mark(void *_data)
|
@@ -47,6 +52,8 @@ void close_internal(struct IO_Event_Selector_EPoll *data) {
|
|
47
52
|
if (data->descriptor >= 0) {
|
48
53
|
close(data->descriptor);
|
49
54
|
data->descriptor = -1;
|
55
|
+
|
56
|
+
IO_Event_Interrupt_close(&data->interrupt);
|
50
57
|
}
|
51
58
|
}
|
52
59
|
|
@@ -85,6 +92,21 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
|
|
85
92
|
return instance;
|
86
93
|
}
|
87
94
|
|
95
|
+
void IO_Event_Interrupt_add(struct IO_Event_Interrupt *interrupt, struct IO_Event_Selector_EPoll *data) {
|
96
|
+
int descriptor = IO_Event_Interrupt_descriptor(interrupt);
|
97
|
+
|
98
|
+
struct epoll_event event = {
|
99
|
+
.events = EPOLLIN|EPOLLRDHUP,
|
100
|
+
.data = {.ptr = NULL},
|
101
|
+
};
|
102
|
+
|
103
|
+
int result = epoll_ctl(data->descriptor, EPOLL_CTL_ADD, descriptor, &event);
|
104
|
+
|
105
|
+
if (result == -1) {
|
106
|
+
rb_sys_fail("IO_Event_Interrupt_add:epoll_ctl");
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
88
110
|
VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
|
89
111
|
struct IO_Event_Selector_EPoll *data = NULL;
|
90
112
|
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
|
@@ -100,9 +122,19 @@ VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
|
|
100
122
|
rb_update_max_fd(data->descriptor);
|
101
123
|
}
|
102
124
|
|
125
|
+
IO_Event_Interrupt_open(&data->interrupt);
|
126
|
+
IO_Event_Interrupt_add(&data->interrupt, data);
|
127
|
+
|
103
128
|
return self;
|
104
129
|
}
|
105
130
|
|
131
|
+
VALUE IO_Event_Selector_EPoll_loop(VALUE self) {
|
132
|
+
struct IO_Event_Selector_EPoll *data = NULL;
|
133
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
|
134
|
+
|
135
|
+
return data->backend.loop;
|
136
|
+
}
|
137
|
+
|
106
138
|
VALUE IO_Event_Selector_EPoll_close(VALUE self) {
|
107
139
|
struct IO_Event_Selector_EPoll *data = NULL;
|
108
140
|
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
|
@@ -498,7 +530,9 @@ void * select_internal(void *_arguments) {
|
|
498
530
|
|
499
531
|
static
|
500
532
|
void select_internal_without_gvl(struct select_arguments *arguments) {
|
533
|
+
arguments->data->blocked = 1;
|
501
534
|
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
535
|
+
arguments->data->blocked = 0;
|
502
536
|
|
503
537
|
if (arguments->count == -1) {
|
504
538
|
rb_sys_fail("select_internal_without_gvl:epoll_wait");
|
@@ -525,29 +559,54 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
|
|
525
559
|
.timeout = 0
|
526
560
|
};
|
527
561
|
|
562
|
+
// Process any currently pending events:
|
528
563
|
select_internal_with_gvl(&arguments);
|
529
|
-
|
530
|
-
// If
|
531
|
-
|
564
|
+
|
565
|
+
// If we:
|
566
|
+
// 1. Didn't process any ready fibers, and
|
567
|
+
// 2. Didn't process any events from non-blocking select (above), and
|
568
|
+
// 3. There are no items in the ready list,
|
569
|
+
// then we can perform a blocking select.
|
570
|
+
if (!ready && !arguments.count && !data->backend.ready) {
|
532
571
|
arguments.timeout = make_timeout(duration);
|
533
572
|
|
534
573
|
if (arguments.timeout != 0) {
|
574
|
+
// Wait for events to occur
|
535
575
|
select_internal_without_gvl(&arguments);
|
536
576
|
}
|
537
577
|
}
|
538
578
|
|
539
579
|
for (int i = 0; i < arguments.count; i += 1) {
|
540
|
-
|
541
|
-
|
580
|
+
const struct epoll_event *event = &arguments.events[i];
|
581
|
+
if (DEBUG) fprintf(stderr, "-> ptr=%p events=%d\n", event->data.ptr, event->events);
|
542
582
|
|
543
|
-
|
544
|
-
|
545
|
-
|
583
|
+
if (event->data.ptr) {
|
584
|
+
VALUE fiber = (VALUE)event->data.ptr;
|
585
|
+
VALUE result = INT2NUM(event->events);
|
586
|
+
|
587
|
+
IO_Event_Selector_fiber_transfer(fiber, 1, &result);
|
588
|
+
} else {
|
589
|
+
IO_Event_Interrupt_clear(&data->interrupt);
|
590
|
+
}
|
546
591
|
}
|
547
592
|
|
548
593
|
return INT2NUM(arguments.count);
|
549
594
|
}
|
550
595
|
|
596
|
+
VALUE IO_Event_Selector_EPoll_wakeup(VALUE self) {
|
597
|
+
struct IO_Event_Selector_EPoll *data = NULL;
|
598
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
|
599
|
+
|
600
|
+
// If we are blocking, we can schedule a nop event to wake up the selector:
|
601
|
+
if (data->blocked) {
|
602
|
+
IO_Event_Interrupt_signal(&data->interrupt);
|
603
|
+
|
604
|
+
return Qtrue;
|
605
|
+
}
|
606
|
+
|
607
|
+
return Qfalse;
|
608
|
+
}
|
609
|
+
|
551
610
|
void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
|
552
611
|
IO_Event_Selector_EPoll = rb_define_class_under(IO_Event_Selector, "EPoll", rb_cObject);
|
553
612
|
rb_gc_register_mark_object(IO_Event_Selector_EPoll);
|
@@ -555,6 +614,8 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
|
|
555
614
|
rb_define_alloc_func(IO_Event_Selector_EPoll, IO_Event_Selector_EPoll_allocate);
|
556
615
|
rb_define_method(IO_Event_Selector_EPoll, "initialize", IO_Event_Selector_EPoll_initialize, 1);
|
557
616
|
|
617
|
+
rb_define_method(IO_Event_Selector_EPoll, "loop", IO_Event_Selector_EPoll_loop, 0);
|
618
|
+
|
558
619
|
rb_define_method(IO_Event_Selector_EPoll, "transfer", IO_Event_Selector_EPoll_transfer, 0);
|
559
620
|
rb_define_method(IO_Event_Selector_EPoll, "resume", IO_Event_Selector_EPoll_resume, -1);
|
560
621
|
rb_define_method(IO_Event_Selector_EPoll, "yield", IO_Event_Selector_EPoll_yield, 0);
|
@@ -564,6 +625,7 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
|
|
564
625
|
rb_define_method(IO_Event_Selector_EPoll, "ready?", IO_Event_Selector_EPoll_ready_p, 0);
|
565
626
|
|
566
627
|
rb_define_method(IO_Event_Selector_EPoll, "select", IO_Event_Selector_EPoll_select, 1);
|
628
|
+
rb_define_method(IO_Event_Selector_EPoll, "wakeup", IO_Event_Selector_EPoll_wakeup, 0);
|
567
629
|
rb_define_method(IO_Event_Selector_EPoll, "close", IO_Event_Selector_EPoll_close, 0);
|
568
630
|
|
569
631
|
rb_define_method(IO_Event_Selector_EPoll, "io_wait", IO_Event_Selector_EPoll_io_wait, 3);
|
@@ -33,6 +33,8 @@ enum {KQUEUE_MAX_EVENTS = 64};
|
|
33
33
|
struct IO_Event_Selector_KQueue {
|
34
34
|
struct IO_Event_Selector backend;
|
35
35
|
int descriptor;
|
36
|
+
|
37
|
+
int blocked;
|
36
38
|
};
|
37
39
|
|
38
40
|
void IO_Event_Selector_KQueue_Type_mark(void *_data)
|
@@ -80,6 +82,7 @@ VALUE IO_Event_Selector_KQueue_allocate(VALUE self) {
|
|
80
82
|
|
81
83
|
IO_Event_Selector_initialize(&data->backend, Qnil);
|
82
84
|
data->descriptor = -1;
|
85
|
+
data->blocked = 0;
|
83
86
|
|
84
87
|
return instance;
|
85
88
|
}
|
@@ -103,6 +106,13 @@ VALUE IO_Event_Selector_KQueue_initialize(VALUE self, VALUE loop) {
|
|
103
106
|
return self;
|
104
107
|
}
|
105
108
|
|
109
|
+
VALUE IO_Event_Selector_KQueue_loop(VALUE self) {
|
110
|
+
struct IO_Event_Selector_KQueue *data = NULL;
|
111
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
|
112
|
+
|
113
|
+
return data->backend.loop;
|
114
|
+
}
|
115
|
+
|
106
116
|
VALUE IO_Event_Selector_KQueue_close(VALUE self) {
|
107
117
|
struct IO_Event_Selector_KQueue *data = NULL;
|
108
118
|
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
|
@@ -561,7 +571,10 @@ void * select_internal(void *_arguments) {
|
|
561
571
|
|
562
572
|
static
|
563
573
|
void select_internal_without_gvl(struct select_arguments *arguments) {
|
574
|
+
arguments->data->blocked = 1;
|
575
|
+
|
564
576
|
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
577
|
+
arguments->data->blocked = 0;
|
565
578
|
|
566
579
|
if (arguments->count == -1) {
|
567
580
|
rb_sys_fail("select_internal_without_gvl:kevent");
|
@@ -592,18 +605,23 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
592
605
|
}
|
593
606
|
};
|
594
607
|
|
608
|
+
arguments.timeout = &arguments.storage;
|
609
|
+
|
595
610
|
// We break this implementation into two parts.
|
596
611
|
// (1) count = kevent(..., timeout = 0)
|
597
612
|
// (2) without gvl: kevent(..., timeout = 0) if count == 0 and timeout != 0
|
598
613
|
// This allows us to avoid releasing and reacquiring the GVL.
|
599
614
|
// Non-comprehensive testing shows this gives a 1.5x speedup.
|
600
|
-
arguments.timeout = &arguments.storage;
|
601
615
|
|
602
616
|
// First do the syscall with no timeout to get any immediately available events:
|
603
617
|
select_internal_with_gvl(&arguments);
|
604
618
|
|
605
|
-
// If
|
606
|
-
|
619
|
+
// If we:
|
620
|
+
// 1. Didn't process any ready fibers, and
|
621
|
+
// 2. Didn't process any events from non-blocking select (above), and
|
622
|
+
// 3. There are no items in the ready list,
|
623
|
+
// then we can perform a blocking select.
|
624
|
+
if (!ready && !arguments.count && !data->backend.ready) {
|
607
625
|
arguments.timeout = make_timeout(duration, &arguments.storage);
|
608
626
|
|
609
627
|
if (!timeout_nonblocking(arguments.timeout)) {
|
@@ -614,15 +632,40 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
614
632
|
}
|
615
633
|
|
616
634
|
for (int i = 0; i < arguments.count; i += 1) {
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
635
|
+
if (arguments.events[i].udata) {
|
636
|
+
VALUE fiber = (VALUE)arguments.events[i].udata;
|
637
|
+
VALUE result = INT2NUM(arguments.events[i].filter);
|
638
|
+
|
639
|
+
IO_Event_Selector_fiber_transfer(fiber, 1, &result);
|
640
|
+
}
|
621
641
|
}
|
622
642
|
|
623
643
|
return INT2NUM(arguments.count);
|
624
644
|
}
|
625
645
|
|
646
|
+
VALUE IO_Event_Selector_KQueue_wakeup(VALUE self) {
|
647
|
+
struct IO_Event_Selector_KQueue *data = NULL;
|
648
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
|
649
|
+
|
650
|
+
if (data->blocked) {
|
651
|
+
struct kevent trigger = {0};
|
652
|
+
|
653
|
+
trigger.filter = EVFILT_USER;
|
654
|
+
trigger.flags = EV_ADD|EV_CLEAR;
|
655
|
+
trigger.fflags = NOTE_TRIGGER;
|
656
|
+
|
657
|
+
int result = kevent(data->descriptor, &trigger, 1, NULL, 0, NULL);
|
658
|
+
|
659
|
+
if (result == -1) {
|
660
|
+
rb_sys_fail("IO_Event_Selector_KQueue_wakeup:kevent");
|
661
|
+
}
|
662
|
+
|
663
|
+
return Qtrue;
|
664
|
+
}
|
665
|
+
|
666
|
+
return Qfalse;
|
667
|
+
}
|
668
|
+
|
626
669
|
void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
|
627
670
|
IO_Event_Selector_KQueue = rb_define_class_under(IO_Event_Selector, "KQueue", rb_cObject);
|
628
671
|
rb_gc_register_mark_object(IO_Event_Selector_KQueue);
|
@@ -630,6 +673,8 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
|
|
630
673
|
rb_define_alloc_func(IO_Event_Selector_KQueue, IO_Event_Selector_KQueue_allocate);
|
631
674
|
rb_define_method(IO_Event_Selector_KQueue, "initialize", IO_Event_Selector_KQueue_initialize, 1);
|
632
675
|
|
676
|
+
rb_define_method(IO_Event_Selector_KQueue, "loop", IO_Event_Selector_KQueue_loop, 0);
|
677
|
+
|
633
678
|
rb_define_method(IO_Event_Selector_KQueue, "transfer", IO_Event_Selector_KQueue_transfer, 0);
|
634
679
|
rb_define_method(IO_Event_Selector_KQueue, "resume", IO_Event_Selector_KQueue_resume, -1);
|
635
680
|
rb_define_method(IO_Event_Selector_KQueue, "yield", IO_Event_Selector_KQueue_yield, 0);
|
@@ -639,6 +684,7 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
|
|
639
684
|
rb_define_method(IO_Event_Selector_KQueue, "ready?", IO_Event_Selector_KQueue_ready_p, 0);
|
640
685
|
|
641
686
|
rb_define_method(IO_Event_Selector_KQueue, "select", IO_Event_Selector_KQueue_select, 1);
|
687
|
+
rb_define_method(IO_Event_Selector_KQueue, "wakeup", IO_Event_Selector_KQueue_wakeup, 0);
|
642
688
|
rb_define_method(IO_Event_Selector_KQueue, "close", IO_Event_Selector_KQueue_close, 0);
|
643
689
|
|
644
690
|
rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
|
@@ -37,6 +37,7 @@ struct IO_Event_Selector_URing {
|
|
37
37
|
struct IO_Event_Selector backend;
|
38
38
|
struct io_uring ring;
|
39
39
|
size_t pending;
|
40
|
+
int blocked;
|
40
41
|
};
|
41
42
|
|
42
43
|
void IO_Event_Selector_URing_Type_mark(void *_data)
|
@@ -86,7 +87,8 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
86
87
|
data->ring.ring_fd = -1;
|
87
88
|
|
88
89
|
data->pending = 0;
|
89
|
-
|
90
|
+
data->blocked = 0;
|
91
|
+
|
90
92
|
return instance;
|
91
93
|
}
|
92
94
|
|
@@ -106,6 +108,13 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
|
|
106
108
|
return self;
|
107
109
|
}
|
108
110
|
|
111
|
+
VALUE IO_Event_Selector_URing_loop(VALUE self) {
|
112
|
+
struct IO_Event_Selector_URing *data = NULL;
|
113
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
|
114
|
+
|
115
|
+
return data->backend.loop;
|
116
|
+
}
|
117
|
+
|
109
118
|
VALUE IO_Event_Selector_URing_close(VALUE self) {
|
110
119
|
struct IO_Event_Selector_URing *data = NULL;
|
111
120
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
|
@@ -156,7 +165,8 @@ VALUE IO_Event_Selector_URing_raise(int argc, VALUE *argv, VALUE self)
|
|
156
165
|
|
157
166
|
return IO_Event_Selector_raise(&data->backend, argc, argv);
|
158
167
|
}
|
159
|
-
|
168
|
+
|
169
|
+
int blocked;
|
160
170
|
VALUE IO_Event_Selector_URing_ready_p(VALUE self) {
|
161
171
|
struct IO_Event_Selector_URing *data = NULL;
|
162
172
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
|
@@ -171,14 +181,14 @@ int io_uring_submit_flush(struct IO_Event_Selector_URing *data) {
|
|
171
181
|
|
172
182
|
// Try to submit:
|
173
183
|
int result = io_uring_submit(&data->ring);
|
174
|
-
|
184
|
+
|
175
185
|
if (result >= 0) {
|
176
186
|
// If it was submitted, reset pending count:
|
177
187
|
data->pending = 0;
|
178
188
|
} else if (result != -EBUSY && result != -EAGAIN) {
|
179
189
|
rb_syserr_fail(-result, "io_uring_submit_flush");
|
180
190
|
}
|
181
|
-
|
191
|
+
|
182
192
|
return result;
|
183
193
|
}
|
184
194
|
|
@@ -194,7 +204,7 @@ int io_uring_submit_now(struct IO_Event_Selector_URing *data) {
|
|
194
204
|
data->pending = 0;
|
195
205
|
return result;
|
196
206
|
}
|
197
|
-
|
207
|
+
|
198
208
|
if (result == -EBUSY || result == -EAGAIN) {
|
199
209
|
IO_Event_Selector_yield(&data->backend);
|
200
210
|
} else {
|
@@ -214,7 +224,7 @@ struct io_uring_sqe * io_get_sqe(struct IO_Event_Selector_URing *data) {
|
|
214
224
|
while (sqe == NULL) {
|
215
225
|
// The submit queue is full, we need to drain it:
|
216
226
|
io_uring_submit_now(data);
|
217
|
-
|
227
|
+
|
218
228
|
sqe = io_uring_get_sqe(&data->ring);
|
219
229
|
}
|
220
230
|
|
@@ -523,10 +533,8 @@ struct select_arguments {
|
|
523
533
|
static
|
524
534
|
void * select_internal(void *_arguments) {
|
525
535
|
struct select_arguments * arguments = (struct select_arguments *)_arguments;
|
526
|
-
|
527
|
-
io_uring_submit_flush(arguments->data);
|
528
|
-
|
529
536
|
struct io_uring_cqe *cqe = NULL;
|
537
|
+
|
530
538
|
arguments->result = io_uring_wait_cqe_timeout(&arguments->data->ring, &cqe, arguments->timeout);
|
531
539
|
|
532
540
|
return NULL;
|
@@ -534,7 +542,11 @@ void * select_internal(void *_arguments) {
|
|
534
542
|
|
535
543
|
static
|
536
544
|
int select_internal_without_gvl(struct select_arguments *arguments) {
|
545
|
+
io_uring_submit_flush(arguments->data);
|
546
|
+
|
547
|
+
arguments->data->blocked = 1;
|
537
548
|
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
549
|
+
arguments->data->blocked = 0;
|
538
550
|
|
539
551
|
if (arguments->result == -ETIME) {
|
540
552
|
arguments->result = 0;
|
@@ -548,6 +560,8 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
548
560
|
return arguments->result;
|
549
561
|
}
|
550
562
|
|
563
|
+
// #define IO_EVENT_SELECTOR_URING_UDATA_INTERRUPT ((__u64) -2)
|
564
|
+
|
551
565
|
static inline
|
552
566
|
unsigned select_process_completions(struct io_uring *ring) {
|
553
567
|
unsigned completed = 0;
|
@@ -563,6 +577,10 @@ unsigned select_process_completions(struct io_uring *ring) {
|
|
563
577
|
continue;
|
564
578
|
}
|
565
579
|
|
580
|
+
// if (cqe->user_data == IO_EVENT_SELECTOR_URING_UDATA_INTERRUPT) {
|
581
|
+
// io_uring_cq_advance(ring, 1);
|
582
|
+
// }
|
583
|
+
|
566
584
|
VALUE fiber = (VALUE)cqe->user_data;
|
567
585
|
VALUE result = RB_INT2NUM(cqe->res);
|
568
586
|
|
@@ -587,9 +605,13 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
587
605
|
int ready = IO_Event_Selector_queue_flush(&data->backend);
|
588
606
|
|
589
607
|
int result = select_process_completions(&data->ring);
|
590
|
-
|
591
|
-
// If
|
592
|
-
|
608
|
+
|
609
|
+
// If we:
|
610
|
+
// 1. Didn't process any ready fibers, and
|
611
|
+
// 2. Didn't process any events from non-blocking select (above), and
|
612
|
+
// 3. There are no items in the ready list,
|
613
|
+
// then we can perform a blocking select.
|
614
|
+
if (!ready && !result && !data->backend.ready) {
|
593
615
|
// We might need to wait for events:
|
594
616
|
struct select_arguments arguments = {
|
595
617
|
.data = data,
|
@@ -605,7 +627,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
605
627
|
// The timeout specified required "nonblocking" behaviour so we just flush the SQ if required:
|
606
628
|
io_uring_submit_flush(data);
|
607
629
|
}
|
608
|
-
|
630
|
+
|
609
631
|
// After waiting/flushing the SQ, check if there are any completions:
|
610
632
|
result = select_process_completions(&data->ring);
|
611
633
|
}
|
@@ -613,6 +635,33 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
613
635
|
return RB_INT2NUM(result);
|
614
636
|
}
|
615
637
|
|
638
|
+
VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
639
|
+
struct IO_Event_Selector_URing *data = NULL;
|
640
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
|
641
|
+
|
642
|
+
// If we are blocking, we can schedule a nop event to wake up the selector:
|
643
|
+
if (data->blocked) {
|
644
|
+
struct io_uring_sqe *sqe = NULL;
|
645
|
+
|
646
|
+
while (true) {
|
647
|
+
sqe = io_uring_get_sqe(&data->ring);
|
648
|
+
if (sqe) break;
|
649
|
+
|
650
|
+
rb_thread_schedule();
|
651
|
+
|
652
|
+
// It's possible we became unblocked already, so we can assume the selector has already cycled at least once:
|
653
|
+
if (!data->blocked) return Qfalse;
|
654
|
+
}
|
655
|
+
|
656
|
+
io_uring_prep_nop(sqe);
|
657
|
+
io_uring_submit(&data->ring);
|
658
|
+
|
659
|
+
return Qtrue;
|
660
|
+
}
|
661
|
+
|
662
|
+
return Qfalse;
|
663
|
+
}
|
664
|
+
|
616
665
|
void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
617
666
|
IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
|
618
667
|
rb_gc_register_mark_object(IO_Event_Selector_URing);
|
@@ -620,6 +669,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
620
669
|
rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
|
621
670
|
rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
|
622
671
|
|
672
|
+
rb_define_method(IO_Event_Selector_URing, "loop", IO_Event_Selector_URing_loop, 0);
|
673
|
+
|
623
674
|
rb_define_method(IO_Event_Selector_URing, "transfer", IO_Event_Selector_URing_transfer, 0);
|
624
675
|
rb_define_method(IO_Event_Selector_URing, "resume", IO_Event_Selector_URing_resume, -1);
|
625
676
|
rb_define_method(IO_Event_Selector_URing, "yield", IO_Event_Selector_URing_yield, 0);
|
@@ -629,6 +680,7 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
629
680
|
rb_define_method(IO_Event_Selector_URing, "ready?", IO_Event_Selector_URing_ready_p, 0);
|
630
681
|
|
631
682
|
rb_define_method(IO_Event_Selector_URing, "select", IO_Event_Selector_URing_select, 1);
|
683
|
+
rb_define_method(IO_Event_Selector_URing, "wakeup", IO_Event_Selector_URing_wakeup, 0);
|
632
684
|
rb_define_method(IO_Event_Selector_URing, "close", IO_Event_Selector_URing_close, 0);
|
633
685
|
|
634
686
|
rb_define_method(IO_Event_Selector_URing, "io_wait", IO_Event_Selector_URing_io_wait, 3);
|
data/ext/kqueue.o
CHANGED
Binary file
|
data/ext/mkmf.log
CHANGED
@@ -113,6 +113,22 @@ checked program was:
|
|
113
113
|
|
114
114
|
--------------------
|
115
115
|
|
116
|
+
have_header: checking for sys/eventfd.h... -------------------- no
|
117
|
+
|
118
|
+
"clang -E -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/arm64-darwin21 -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -pipe -Wall conftest.c -o conftest.i"
|
119
|
+
conftest.c:3:10: fatal error: 'sys/eventfd.h' file not found
|
120
|
+
#include <sys/eventfd.h>
|
121
|
+
^~~~~~~~~~~~~~~
|
122
|
+
1 error generated.
|
123
|
+
checked program was:
|
124
|
+
/* begin */
|
125
|
+
1: #include "ruby.h"
|
126
|
+
2:
|
127
|
+
3: #include <sys/eventfd.h>
|
128
|
+
/* end */
|
129
|
+
|
130
|
+
--------------------
|
131
|
+
|
116
132
|
have_func: checking for rb_io_descriptor()... -------------------- no
|
117
133
|
|
118
134
|
"clang -fdeclspec -o conftest -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/arm64-darwin21 -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -pipe -Wall conftest.c -L. -L/Users/samuel/.rubies/ruby-3.0.3/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -m64 -lruby.3.0-static -framework Security -framework Foundation -lpthread -lgmp -ldl -lobjc "
|
@@ -141,7 +157,7 @@ checked program was:
|
|
141
157
|
"clang -fdeclspec -o conftest -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/arm64-darwin21 -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -pipe -Wall conftest.c -L. -L/Users/samuel/.rubies/ruby-3.0.3/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -m64 -lruby.3.0-static -framework Security -framework Foundation -lpthread -lgmp -ldl -lobjc "
|
142
158
|
Undefined symbols for architecture arm64:
|
143
159
|
"_rb_io_descriptor", referenced from:
|
144
|
-
_t in conftest-
|
160
|
+
_t in conftest-1e4d1a.o
|
145
161
|
ld: symbol(s) not found for architecture arm64
|
146
162
|
clang: error: linker command failed with exit code 1 (use -v to see invocation)
|
147
163
|
checked program was:
|
@@ -28,6 +28,14 @@ module IO::Event
|
|
28
28
|
@readable = {}
|
29
29
|
@writable = {}
|
30
30
|
@priority = {}
|
31
|
+
|
32
|
+
unless Fiber.current == selector.loop
|
33
|
+
raise "Selector must be initialized on event loop fiber!"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def wakeup
|
38
|
+
@selector.wakeup
|
31
39
|
end
|
32
40
|
|
33
41
|
def close
|
@@ -86,13 +94,17 @@ module IO::Event
|
|
86
94
|
end
|
87
95
|
|
88
96
|
def select(duration = nil)
|
97
|
+
unless Fiber.current == @selector.loop
|
98
|
+
raise "Selector must be run on event loop fiber!"
|
99
|
+
end
|
100
|
+
|
89
101
|
@selector.select(duration)
|
90
102
|
end
|
91
103
|
|
92
104
|
private
|
93
105
|
|
94
106
|
def register_readable(fiber, io, events)
|
95
|
-
if (events & READABLE) > 0
|
107
|
+
if (events & IO::READABLE) > 0
|
96
108
|
if @readable.key?(io)
|
97
109
|
raise "Cannot wait for #{io} to become readable from multiple fibers."
|
98
110
|
end
|
@@ -110,7 +122,7 @@ module IO::Event
|
|
110
122
|
end
|
111
123
|
|
112
124
|
def register_writable(fiber, io, events)
|
113
|
-
if (events & WRITABLE) > 0
|
125
|
+
if (events & IO::WRITABLE) > 0
|
114
126
|
if @writable.key?(io)
|
115
127
|
raise "Cannot wait for #{io} to become writable from multiple fibers."
|
116
128
|
end
|
@@ -128,7 +140,7 @@ module IO::Event
|
|
128
140
|
end
|
129
141
|
|
130
142
|
def register_priority(fiber, io, events)
|
131
|
-
if (events & PRIORITY) > 0
|
143
|
+
if (events & IO::PRIORITY) > 0
|
132
144
|
if @priority.key?(io)
|
133
145
|
raise "Cannot wait for #{io} to become priority from multiple fibers."
|
134
146
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
module IO::Event
|
22
|
+
# A thread safe synchronisation primative.
|
23
|
+
class Interrupt
|
24
|
+
def self.attach(selector)
|
25
|
+
self.new(selector)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(selector)
|
29
|
+
@selector = selector
|
30
|
+
@input, @output = ::IO.pipe
|
31
|
+
|
32
|
+
@fiber = Fiber.new do
|
33
|
+
while true
|
34
|
+
if @selector.io_wait(@fiber, @input, IO::READABLE)
|
35
|
+
@input.read_nonblock(1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@fiber.transfer
|
41
|
+
end
|
42
|
+
|
43
|
+
# Send a sigle byte interrupt.
|
44
|
+
def signal
|
45
|
+
@output.write('.')
|
46
|
+
@output.flush
|
47
|
+
end
|
48
|
+
|
49
|
+
def close
|
50
|
+
@input.close
|
51
|
+
@output.close
|
52
|
+
# @fiber.raise(::Interrupt)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private_constant :Interrupt
|
57
|
+
end
|
@@ -18,19 +18,39 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require_relative '../interrupt'
|
22
|
+
|
21
23
|
module IO::Event
|
22
24
|
module Selector
|
23
25
|
class Select
|
24
26
|
def initialize(loop)
|
25
27
|
@loop = loop
|
26
28
|
|
27
|
-
@readable =
|
28
|
-
@writable =
|
29
|
+
@readable = Hash.new.compare_by_identity
|
30
|
+
@writable = Hash.new.compare_by_identity
|
31
|
+
|
32
|
+
@blocked = false
|
29
33
|
|
30
34
|
@ready = Queue.new
|
35
|
+
@interrupt = Interrupt.attach(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
attr :loop
|
39
|
+
|
40
|
+
# If the event loop is currently blocked,
|
41
|
+
def wakeup
|
42
|
+
if @blocked
|
43
|
+
@interrupt.signal
|
44
|
+
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
|
48
|
+
return false
|
31
49
|
end
|
32
50
|
|
33
51
|
def close
|
52
|
+
@interrupt.close
|
53
|
+
|
34
54
|
@loop = nil
|
35
55
|
@readable = nil
|
36
56
|
@writable = nil
|
@@ -97,12 +117,12 @@ module IO::Event
|
|
97
117
|
def io_wait(fiber, io, events)
|
98
118
|
remove_readable = remove_writable = false
|
99
119
|
|
100
|
-
if (events & READABLE) > 0 or (events & PRIORITY) > 0
|
120
|
+
if (events & IO::READABLE) > 0 or (events & IO::PRIORITY) > 0
|
101
121
|
@readable[io] = fiber
|
102
122
|
remove_readable = true
|
103
123
|
end
|
104
124
|
|
105
|
-
if (events & WRITABLE) > 0
|
125
|
+
if (events & IO::WRITABLE) > 0
|
106
126
|
@writable[io] = fiber
|
107
127
|
remove_writable = true
|
108
128
|
end
|
@@ -123,9 +143,9 @@ module IO::Event
|
|
123
143
|
|
124
144
|
case result = io.read_nonblock(maximum_size, exception: false)
|
125
145
|
when :wait_readable
|
126
|
-
self.io_wait(fiber, io, READABLE)
|
146
|
+
self.io_wait(fiber, io, IO::READABLE)
|
127
147
|
when :wait_writable
|
128
|
-
self.io_wait(fiber, io, WRITABLE)
|
148
|
+
self.io_wait(fiber, io, IO::WRITABLE)
|
129
149
|
else
|
130
150
|
break unless result
|
131
151
|
|
@@ -147,9 +167,9 @@ module IO::Event
|
|
147
167
|
chunk = buffer.to_str(offset, length)
|
148
168
|
case result = io.write_nonblock(chunk, exception: false)
|
149
169
|
when :wait_readable
|
150
|
-
self.io_wait(fiber, io, READABLE)
|
170
|
+
self.io_wait(fiber, io, IO::READABLE)
|
151
171
|
when :wait_writable
|
152
|
-
self.io_wait(fiber, io, WRITABLE)
|
172
|
+
self.io_wait(fiber, io, IO::WRITABLE)
|
153
173
|
else
|
154
174
|
offset += result
|
155
175
|
length -= result
|
@@ -169,7 +189,7 @@ module IO::Event
|
|
169
189
|
w.close
|
170
190
|
end
|
171
191
|
|
172
|
-
self.io_wait(fiber, r, READABLE)
|
192
|
+
self.io_wait(fiber, r, IO::READABLE)
|
173
193
|
|
174
194
|
return thread.value
|
175
195
|
ensure
|
@@ -193,26 +213,32 @@ module IO::Event
|
|
193
213
|
|
194
214
|
def select(duration = nil)
|
195
215
|
if pop_ready
|
216
|
+
# If we have popped items from the ready list, they may influence the duration calculation, so we don't delay the event loop:
|
196
217
|
duration = 0
|
197
218
|
end
|
198
219
|
|
220
|
+
@blocked = true
|
221
|
+
duration = 0 unless @ready.empty?
|
199
222
|
readable, writable, _ = ::IO.select(@readable.keys, @writable.keys, nil, duration)
|
223
|
+
@blocked = false
|
200
224
|
|
201
225
|
ready = Hash.new(0)
|
202
226
|
|
203
227
|
readable&.each do |io|
|
204
228
|
fiber = @readable.delete(io)
|
205
|
-
ready[fiber] |= READABLE
|
229
|
+
ready[fiber] |= IO::READABLE
|
206
230
|
end
|
207
231
|
|
208
232
|
writable&.each do |io|
|
209
233
|
fiber = @writable.delete(io)
|
210
|
-
ready[fiber] |= WRITABLE
|
234
|
+
ready[fiber] |= IO::WRITABLE
|
211
235
|
end
|
212
236
|
|
213
237
|
ready.each do |fiber, events|
|
214
238
|
fiber.transfer(events) if fiber.alive?
|
215
239
|
end
|
240
|
+
|
241
|
+
return ready.size
|
216
242
|
end
|
217
243
|
end
|
218
244
|
end
|
data/lib/io/event/selector.rb
CHANGED
@@ -21,11 +21,6 @@
|
|
21
21
|
require_relative 'selector/select'
|
22
22
|
|
23
23
|
module IO::Event
|
24
|
-
# These constants are the same as those defined in IO.
|
25
|
-
READABLE = 1
|
26
|
-
PRIORITY = 2
|
27
|
-
WRITABLE = 4
|
28
|
-
|
29
24
|
module Selector
|
30
25
|
def self.default(env = ENV)
|
31
26
|
if name = env['IO_EVENT_SELECTOR']&.to_sym
|
data/lib/io/event/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-event
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bake
|
@@ -53,33 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: sus
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0.5'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: timers
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
68
|
+
version: '0.5'
|
83
69
|
description:
|
84
70
|
email:
|
85
71
|
executables: []
|
@@ -92,8 +78,11 @@ files:
|
|
92
78
|
- ext/event.o
|
93
79
|
- ext/extconf.h
|
94
80
|
- ext/extconf.rb
|
81
|
+
- ext/interrupt.o
|
95
82
|
- ext/io/event/event.c
|
96
83
|
- ext/io/event/event.h
|
84
|
+
- ext/io/event/interrupt.c
|
85
|
+
- ext/io/event/interrupt.h
|
97
86
|
- ext/io/event/selector/epoll.c
|
98
87
|
- ext/io/event/selector/epoll.h
|
99
88
|
- ext/io/event/selector/kqueue.c
|
@@ -108,6 +97,7 @@ files:
|
|
108
97
|
- ext/selector.o
|
109
98
|
- lib/io/event.rb
|
110
99
|
- lib/io/event/debug/selector.rb
|
100
|
+
- lib/io/event/interrupt.rb
|
111
101
|
- lib/io/event/selector.rb
|
112
102
|
- lib/io/event/selector/select.rb
|
113
103
|
- lib/io/event/version.rb
|