io-event 0.1.0 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|