io-event 1.6.5 → 1.7.5
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/extconf.rb +25 -12
- data/ext/io/event/event.c +2 -8
- data/ext/io/event/interrupt.c +7 -0
- data/ext/io/event/selector/array.h +52 -18
- data/ext/io/event/selector/epoll.c +26 -11
- data/ext/io/event/selector/kqueue.c +20 -10
- data/ext/io/event/selector/list.h +19 -1
- data/ext/io/event/selector/selector.c +24 -2
- data/ext/io/event/selector/selector.h +11 -8
- data/ext/io/event/selector/uring.c +114 -14
- data/lib/io/event/debug/selector.rb +44 -4
- data/lib/io/event/interrupt.rb +2 -2
- data/lib/io/event/priority_heap.rb +13 -14
- data/lib/io/event/selector/nonblock.rb +6 -2
- data/lib/io/event/selector/select.rb +25 -5
- data/lib/io/event/selector.rb +15 -5
- data/lib/io/event/support.rb +23 -2
- data/lib/io/event/timers.rb +42 -4
- data/lib/io/event/version.rb +3 -1
- data/lib/io/event.rb +5 -5
- data/license.md +3 -0
- data/readme.md +14 -4
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +8 -4
- 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: ab271a1de3eb0f5d21b0b7c92cafcc226e5ca218e7b87ebaa80f93a54fd7b945
|
|
4
|
+
data.tar.gz: fa6227d0b4218b277903fb1c6f1889e51bcfb7e8d8f83086edc30a336a065eff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5afc83ef6364791d86b14bdf130d68fda3a7a1c720349dce6bb2140a2ee9f69015e542dbf5c724a0daa338e5f13c87f1dee566e8a36a1b0847ce5e86e1fa92b
|
|
7
|
+
data.tar.gz: 7d09237e5141123bf251fe686e25fca7d8391a7c25fe331e8882d7e43bf87ff9c5e89bc5c5f90d0731705b923eade10c4667344ad06d04a08544bb9a944baa28
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/ext/extconf.rb
CHANGED
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
# Released under the MIT License.
|
|
5
|
-
# Copyright, 2021-
|
|
5
|
+
# Copyright, 2021-2024, by Samuel Williams.
|
|
6
6
|
# Copyright, 2023, by Math Ieu.
|
|
7
7
|
|
|
8
8
|
return if RUBY_DESCRIPTION =~ /jruby/
|
|
9
9
|
|
|
10
|
-
require
|
|
10
|
+
require "mkmf"
|
|
11
11
|
|
|
12
12
|
gem_name = File.basename(__dir__)
|
|
13
|
-
extension_name =
|
|
13
|
+
extension_name = "IO_Event"
|
|
14
14
|
|
|
15
15
|
# dir_config(extension_name)
|
|
16
16
|
|
|
17
17
|
$CFLAGS << " -Wall -Wno-unknown-pragmas -std=c99"
|
|
18
18
|
|
|
19
|
-
if ENV.key?(
|
|
19
|
+
if ENV.key?("RUBY_DEBUG")
|
|
20
|
+
$stderr.puts "Enabling debug mode..."
|
|
21
|
+
|
|
20
22
|
$CFLAGS << " -DRUBY_DEBUG -O0"
|
|
21
23
|
end
|
|
22
24
|
|
|
@@ -24,24 +26,27 @@ $srcs = ["io/event/event.c", "io/event/selector/selector.c"]
|
|
|
24
26
|
$VPATH << "$(srcdir)/io/event"
|
|
25
27
|
$VPATH << "$(srcdir)/io/event/selector"
|
|
26
28
|
|
|
27
|
-
have_func(
|
|
28
|
-
have_func(
|
|
29
|
+
have_func("rb_ext_ractor_safe")
|
|
30
|
+
have_func("&rb_fiber_transfer")
|
|
29
31
|
|
|
30
|
-
if have_library(
|
|
32
|
+
if have_library("uring") and have_header("liburing.h")
|
|
33
|
+
# We might want to consider using this in the future:
|
|
34
|
+
# have_func("io_uring_submit_and_wait_timeout", "liburing.h")
|
|
35
|
+
|
|
31
36
|
$srcs << "io/event/selector/uring.c"
|
|
32
37
|
end
|
|
33
38
|
|
|
34
|
-
if have_header(
|
|
39
|
+
if have_header("sys/epoll.h")
|
|
35
40
|
$srcs << "io/event/selector/epoll.c"
|
|
36
41
|
end
|
|
37
42
|
|
|
38
|
-
if have_header(
|
|
43
|
+
if have_header("sys/event.h")
|
|
39
44
|
$srcs << "io/event/selector/kqueue.c"
|
|
40
45
|
end
|
|
41
46
|
|
|
42
|
-
have_header(
|
|
47
|
+
have_header("sys/wait.h")
|
|
43
48
|
|
|
44
|
-
have_header(
|
|
49
|
+
have_header("sys/eventfd.h")
|
|
45
50
|
$srcs << "io/event/interrupt.c"
|
|
46
51
|
|
|
47
52
|
have_func("rb_io_descriptor")
|
|
@@ -50,7 +55,15 @@ have_func("rb_fiber_current")
|
|
|
50
55
|
have_func("&rb_fiber_raise")
|
|
51
56
|
have_func("epoll_pwait2")
|
|
52
57
|
|
|
53
|
-
have_header(
|
|
58
|
+
have_header("ruby/io/buffer.h")
|
|
59
|
+
|
|
60
|
+
if ENV.key?("RUBY_SANITIZE")
|
|
61
|
+
$stderr.puts "Enabling sanitizers..."
|
|
62
|
+
|
|
63
|
+
# Add address and undefined behaviour sanitizers:
|
|
64
|
+
$CFLAGS << " -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer"
|
|
65
|
+
$LDFLAGS << " -fsanitize=address -fsanitize=undefined"
|
|
66
|
+
end
|
|
54
67
|
|
|
55
68
|
create_header
|
|
56
69
|
|
data/ext/io/event/event.c
CHANGED
|
@@ -21,20 +21,14 @@
|
|
|
21
21
|
#include "event.h"
|
|
22
22
|
#include "selector/selector.h"
|
|
23
23
|
|
|
24
|
-
VALUE IO_Event = Qnil;
|
|
25
|
-
VALUE IO_Event_Selector = Qnil;
|
|
26
|
-
|
|
27
24
|
void Init_IO_Event(void)
|
|
28
25
|
{
|
|
29
26
|
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
30
27
|
rb_ext_ractor_safe(true);
|
|
31
28
|
#endif
|
|
32
29
|
|
|
33
|
-
IO_Event = rb_define_module_under(rb_cIO, "Event");
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
IO_Event_Selector = rb_define_module_under(IO_Event, "Selector");
|
|
37
|
-
rb_gc_register_mark_object(IO_Event_Selector);
|
|
30
|
+
VALUE IO_Event = rb_define_module_under(rb_cIO, "Event");
|
|
31
|
+
VALUE IO_Event_Selector = rb_define_module_under(IO_Event, "Selector");
|
|
38
32
|
|
|
39
33
|
Init_IO_Event_Selector(IO_Event_Selector);
|
|
40
34
|
|
data/ext/io/event/interrupt.c
CHANGED
|
@@ -25,6 +25,13 @@
|
|
|
25
25
|
|
|
26
26
|
#include "selector/selector.h"
|
|
27
27
|
|
|
28
|
+
#ifdef HAVE_RUBY_WIN32_H
|
|
29
|
+
#include <ruby/win32.h>
|
|
30
|
+
#if !defined(HAVE_PIPE) && !defined(pipe)
|
|
31
|
+
#define pipe(p) rb_w32_pipe(p)
|
|
32
|
+
#endif
|
|
33
|
+
#endif
|
|
34
|
+
|
|
28
35
|
#ifdef HAVE_SYS_EVENTFD_H
|
|
29
36
|
#include <sys/eventfd.h>
|
|
30
37
|
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
#include <errno.h>
|
|
9
9
|
#include <assert.h>
|
|
10
10
|
|
|
11
|
+
static const size_t IO_EVENT_ARRAY_MAXIMUM_COUNT = SIZE_MAX / sizeof(void*);
|
|
12
|
+
static const size_t IO_EVENT_ARRAY_DEFAULT_COUNT = 128;
|
|
13
|
+
|
|
11
14
|
struct IO_Event_Array {
|
|
12
15
|
// The array of pointers to elements:
|
|
13
16
|
void **base;
|
|
@@ -25,18 +28,27 @@ struct IO_Event_Array {
|
|
|
25
28
|
void (*element_free)(void*);
|
|
26
29
|
};
|
|
27
30
|
|
|
28
|
-
inline static
|
|
31
|
+
inline static int IO_Event_Array_allocate(struct IO_Event_Array *array, size_t count, size_t element_size)
|
|
29
32
|
{
|
|
33
|
+
array->limit = 0;
|
|
34
|
+
array->element_size = element_size;
|
|
35
|
+
|
|
30
36
|
if (count) {
|
|
31
37
|
array->base = (void**)calloc(count, sizeof(void*));
|
|
38
|
+
|
|
39
|
+
if (array->base == NULL) {
|
|
40
|
+
return -1;
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
array->count = count;
|
|
44
|
+
|
|
45
|
+
return 1;
|
|
33
46
|
} else {
|
|
34
47
|
array->base = NULL;
|
|
35
48
|
array->count = 0;
|
|
49
|
+
|
|
50
|
+
return 0;
|
|
36
51
|
}
|
|
37
|
-
|
|
38
|
-
array->limit = 0;
|
|
39
|
-
array->element_size = element_size;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
inline static size_t IO_Event_Array_memory_size(const struct IO_Event_Array *array)
|
|
@@ -47,31 +59,51 @@ inline static size_t IO_Event_Array_memory_size(const struct IO_Event_Array *arr
|
|
|
47
59
|
|
|
48
60
|
inline static void IO_Event_Array_free(struct IO_Event_Array *array)
|
|
49
61
|
{
|
|
50
|
-
|
|
51
|
-
void
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
if (array->base) {
|
|
63
|
+
void **base = array->base;
|
|
64
|
+
size_t limit = array->limit;
|
|
65
|
+
|
|
66
|
+
array->base = NULL;
|
|
67
|
+
array->count = 0;
|
|
68
|
+
array->limit = 0;
|
|
69
|
+
|
|
70
|
+
for (size_t i = 0; i < limit; i += 1) {
|
|
71
|
+
void *element = base[i];
|
|
72
|
+
if (element) {
|
|
73
|
+
array->element_free(element);
|
|
74
|
+
|
|
75
|
+
free(element);
|
|
76
|
+
}
|
|
55
77
|
}
|
|
78
|
+
|
|
79
|
+
free(base);
|
|
56
80
|
}
|
|
57
|
-
|
|
58
|
-
if (array->base)
|
|
59
|
-
free(array->base);
|
|
60
|
-
|
|
61
|
-
array->base = NULL;
|
|
62
|
-
array->count = 0;
|
|
63
|
-
array->limit = 0;
|
|
64
81
|
}
|
|
65
82
|
|
|
66
83
|
inline static int IO_Event_Array_resize(struct IO_Event_Array *array, size_t count)
|
|
67
84
|
{
|
|
68
85
|
if (count <= array->count) {
|
|
86
|
+
// Already big enough:
|
|
69
87
|
return 0;
|
|
70
88
|
}
|
|
71
89
|
|
|
72
|
-
|
|
90
|
+
if (count > IO_EVENT_ARRAY_MAXIMUM_COUNT) {
|
|
91
|
+
errno = ENOMEM;
|
|
92
|
+
return -1;
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
size_t new_count = array->count;
|
|
74
|
-
|
|
96
|
+
|
|
97
|
+
// If the array is empty, we need to set the initial size:
|
|
98
|
+
if (new_count == 0) new_count = IO_EVENT_ARRAY_DEFAULT_COUNT;
|
|
99
|
+
else while (new_count < count) {
|
|
100
|
+
// Ensure we don't overflow:
|
|
101
|
+
if (new_count > (IO_EVENT_ARRAY_MAXIMUM_COUNT / 2)) {
|
|
102
|
+
new_count = IO_EVENT_ARRAY_MAXIMUM_COUNT;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Compute the next multiple (ideally a power of 2):
|
|
75
107
|
new_count *= 2;
|
|
76
108
|
}
|
|
77
109
|
|
|
@@ -87,6 +119,7 @@ inline static int IO_Event_Array_resize(struct IO_Event_Array *array, size_t cou
|
|
|
87
119
|
array->base = (void**)new_base;
|
|
88
120
|
array->count = new_count;
|
|
89
121
|
|
|
122
|
+
// Resizing sucessful:
|
|
90
123
|
return 1;
|
|
91
124
|
}
|
|
92
125
|
|
|
@@ -107,6 +140,7 @@ inline static void* IO_Event_Array_lookup(struct IO_Event_Array *array, size_t i
|
|
|
107
140
|
// Allocate the element if it doesn't exist:
|
|
108
141
|
if (*element == NULL) {
|
|
109
142
|
*element = malloc(array->element_size);
|
|
143
|
+
assert(*element);
|
|
110
144
|
|
|
111
145
|
if (array->element_initialize) {
|
|
112
146
|
array->element_initialize(*element);
|
|
@@ -34,8 +34,6 @@ enum {
|
|
|
34
34
|
DEBUG = 0,
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
static VALUE IO_Event_Selector_EPoll = Qnil;
|
|
38
|
-
|
|
39
37
|
enum {EPOLL_MAX_EVENTS = 64};
|
|
40
38
|
|
|
41
39
|
// This represents an actual fiber waiting for a specific event.
|
|
@@ -184,7 +182,7 @@ static const rb_data_type_t IO_Event_Selector_EPoll_Type = {
|
|
|
184
182
|
.dsize = IO_Event_Selector_EPoll_Type_size,
|
|
185
183
|
},
|
|
186
184
|
.data = NULL,
|
|
187
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
185
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
188
186
|
};
|
|
189
187
|
|
|
190
188
|
inline static
|
|
@@ -243,7 +241,7 @@ int IO_Event_Selector_EPoll_Descriptor_update(struct IO_Event_Selector_EPoll *se
|
|
|
243
241
|
} else {
|
|
244
242
|
// The IO has changed, we need to reset the state:
|
|
245
243
|
epoll_descriptor->registered_events = 0;
|
|
246
|
-
epoll_descriptor->io
|
|
244
|
+
RB_OBJ_WRITE(selector->backend.self, &epoll_descriptor->io, io);
|
|
247
245
|
}
|
|
248
246
|
|
|
249
247
|
if (epoll_descriptor->waiting_events == 0) {
|
|
@@ -253,7 +251,7 @@ int IO_Event_Selector_EPoll_Descriptor_update(struct IO_Event_Selector_EPoll *se
|
|
|
253
251
|
epoll_descriptor->registered_events = 0;
|
|
254
252
|
}
|
|
255
253
|
|
|
256
|
-
epoll_descriptor->io
|
|
254
|
+
RB_OBJ_WRITE(selector->backend.self, &epoll_descriptor->io, 0);
|
|
257
255
|
|
|
258
256
|
return 0;
|
|
259
257
|
}
|
|
@@ -333,13 +331,16 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
|
|
|
333
331
|
struct IO_Event_Selector_EPoll *selector = NULL;
|
|
334
332
|
VALUE instance = TypedData_Make_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
|
|
335
333
|
|
|
336
|
-
IO_Event_Selector_initialize(&selector->backend, Qnil);
|
|
334
|
+
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
337
335
|
selector->descriptor = -1;
|
|
338
336
|
selector->blocked = 0;
|
|
339
337
|
|
|
340
338
|
selector->descriptors.element_initialize = IO_Event_Selector_EPoll_Descriptor_initialize;
|
|
341
339
|
selector->descriptors.element_free = IO_Event_Selector_EPoll_Descriptor_free;
|
|
342
|
-
IO_Event_Array_allocate(&selector->descriptors,
|
|
340
|
+
int result = IO_Event_Array_allocate(&selector->descriptors, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_EPoll_Descriptor));
|
|
341
|
+
if (result < 0) {
|
|
342
|
+
rb_sys_fail("IO_Event_Selector_EPoll_allocate:IO_Event_Array_allocate");
|
|
343
|
+
}
|
|
343
344
|
|
|
344
345
|
return instance;
|
|
345
346
|
}
|
|
@@ -363,7 +364,7 @@ VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
|
|
|
363
364
|
struct IO_Event_Selector_EPoll *selector = NULL;
|
|
364
365
|
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
|
|
365
366
|
|
|
366
|
-
IO_Event_Selector_initialize(&selector->backend, loop);
|
|
367
|
+
IO_Event_Selector_initialize(&selector->backend, self, loop);
|
|
367
368
|
int result = epoll_create1(EPOLL_CLOEXEC);
|
|
368
369
|
|
|
369
370
|
if (result == -1) {
|
|
@@ -503,13 +504,22 @@ VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
503
504
|
|
|
504
505
|
rb_update_max_fd(descriptor);
|
|
505
506
|
|
|
507
|
+
// `pidfd_open` (above) may be edge triggered, so we need to check if the process is already exited, and if so, return immediately, otherwise we will block indefinitely.
|
|
508
|
+
VALUE status = IO_Event_Selector_process_status_wait(pid, flags);
|
|
509
|
+
if (status != Qnil) {
|
|
510
|
+
close(descriptor);
|
|
511
|
+
return status;
|
|
512
|
+
}
|
|
513
|
+
|
|
506
514
|
struct IO_Event_Selector_EPoll_Waiting waiting = {
|
|
507
515
|
.list = {.type = &IO_Event_Selector_EPoll_process_wait_list_type},
|
|
508
516
|
.fiber = fiber,
|
|
509
517
|
.events = IO_EVENT_READABLE,
|
|
510
518
|
};
|
|
511
519
|
|
|
512
|
-
|
|
520
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
521
|
+
|
|
522
|
+
int result = IO_Event_Selector_EPoll_Waiting_register(selector, _pid, descriptor, &waiting);
|
|
513
523
|
|
|
514
524
|
if (result == -1) {
|
|
515
525
|
close(descriptor);
|
|
@@ -568,6 +578,8 @@ VALUE IO_Event_Selector_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
|
|
|
568
578
|
.events = RB_NUM2INT(events),
|
|
569
579
|
};
|
|
570
580
|
|
|
581
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
582
|
+
|
|
571
583
|
int result = IO_Event_Selector_EPoll_Waiting_register(selector, io, descriptor, &waiting);
|
|
572
584
|
|
|
573
585
|
if (result == -1) {
|
|
@@ -666,6 +678,8 @@ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
666
678
|
.offset = offset,
|
|
667
679
|
};
|
|
668
680
|
|
|
681
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
682
|
+
|
|
669
683
|
return rb_ensure(io_read_loop, (VALUE)&io_read_arguments, io_read_ensure, (VALUE)&io_read_arguments);
|
|
670
684
|
}
|
|
671
685
|
|
|
@@ -762,6 +776,8 @@ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
762
776
|
.offset = offset,
|
|
763
777
|
};
|
|
764
778
|
|
|
779
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
780
|
+
|
|
765
781
|
return rb_ensure(io_write_loop, (VALUE)&io_write_arguments, io_write_ensure, (VALUE)&io_write_arguments);
|
|
766
782
|
}
|
|
767
783
|
|
|
@@ -1037,8 +1053,7 @@ VALUE IO_Event_Selector_EPoll_wakeup(VALUE self) {
|
|
|
1037
1053
|
}
|
|
1038
1054
|
|
|
1039
1055
|
void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
|
|
1040
|
-
IO_Event_Selector_EPoll = rb_define_class_under(IO_Event_Selector, "EPoll", rb_cObject);
|
|
1041
|
-
rb_gc_register_mark_object(IO_Event_Selector_EPoll);
|
|
1056
|
+
VALUE IO_Event_Selector_EPoll = rb_define_class_under(IO_Event_Selector, "EPoll", rb_cObject);
|
|
1042
1057
|
|
|
1043
1058
|
rb_define_alloc_func(IO_Event_Selector_EPoll, IO_Event_Selector_EPoll_allocate);
|
|
1044
1059
|
rb_define_method(IO_Event_Selector_EPoll, "initialize", IO_Event_Selector_EPoll_initialize, 1);
|
|
@@ -43,8 +43,6 @@ enum {
|
|
|
43
43
|
#define IO_EVENT_SELECTOR_KQUEUE_USE_INTERRUPT
|
|
44
44
|
#endif
|
|
45
45
|
|
|
46
|
-
static VALUE IO_Event_Selector_KQueue = Qnil;
|
|
47
|
-
|
|
48
46
|
enum {KQUEUE_MAX_EVENTS = 64};
|
|
49
47
|
|
|
50
48
|
// This represents an actual fiber waiting for a specific event.
|
|
@@ -183,7 +181,7 @@ static const rb_data_type_t IO_Event_Selector_KQueue_Type = {
|
|
|
183
181
|
.dsize = IO_Event_Selector_KQueue_Type_size,
|
|
184
182
|
},
|
|
185
183
|
.data = NULL,
|
|
186
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
184
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
187
185
|
};
|
|
188
186
|
|
|
189
187
|
inline static
|
|
@@ -307,13 +305,17 @@ VALUE IO_Event_Selector_KQueue_allocate(VALUE self) {
|
|
|
307
305
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
|
308
306
|
VALUE instance = TypedData_Make_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
|
309
307
|
|
|
310
|
-
IO_Event_Selector_initialize(&selector->backend, Qnil);
|
|
308
|
+
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
311
309
|
selector->descriptor = -1;
|
|
312
310
|
selector->blocked = 0;
|
|
313
311
|
|
|
314
312
|
selector->descriptors.element_initialize = IO_Event_Selector_KQueue_Descriptor_initialize;
|
|
315
313
|
selector->descriptors.element_free = IO_Event_Selector_KQueue_Descriptor_free;
|
|
316
|
-
|
|
314
|
+
|
|
315
|
+
int result = IO_Event_Array_allocate(&selector->descriptors, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_KQueue_Descriptor));
|
|
316
|
+
if (result < 0) {
|
|
317
|
+
rb_sys_fail("IO_Event_Selector_KQueue_allocate:IO_Event_Array_allocate");
|
|
318
|
+
}
|
|
317
319
|
|
|
318
320
|
return instance;
|
|
319
321
|
}
|
|
@@ -340,7 +342,7 @@ VALUE IO_Event_Selector_KQueue_initialize(VALUE self, VALUE loop) {
|
|
|
340
342
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
|
341
343
|
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
|
342
344
|
|
|
343
|
-
IO_Event_Selector_initialize(&selector->backend, loop);
|
|
345
|
+
IO_Event_Selector_initialize(&selector->backend, self, loop);
|
|
344
346
|
int result = kqueue();
|
|
345
347
|
|
|
346
348
|
if (result == -1) {
|
|
@@ -459,6 +461,7 @@ void process_prewait(pid_t pid) {
|
|
|
459
461
|
result = waitid(P_PID, pid, &info, WEXITED | WNOWAIT);
|
|
460
462
|
// This can sometimes get interrupted by SIGCHLD.
|
|
461
463
|
} while (result == -1 && errno == EINTR);
|
|
464
|
+
|
|
462
465
|
if (result == -1) {
|
|
463
466
|
rb_sys_fail("process_prewait:waitid");
|
|
464
467
|
}
|
|
@@ -503,6 +506,8 @@ VALUE IO_Event_Selector_KQueue_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
503
506
|
.events = IO_EVENT_EXIT,
|
|
504
507
|
};
|
|
505
508
|
|
|
509
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
510
|
+
|
|
506
511
|
struct process_wait_arguments process_wait_arguments = {
|
|
507
512
|
.selector = selector,
|
|
508
513
|
.waiting = &waiting,
|
|
@@ -566,6 +571,8 @@ VALUE IO_Event_Selector_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
566
571
|
.events = RB_NUM2INT(events),
|
|
567
572
|
};
|
|
568
573
|
|
|
574
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
575
|
+
|
|
569
576
|
int result = IO_Event_Selector_KQueue_Waiting_register(selector, descriptor, &waiting);
|
|
570
577
|
if (result == -1) {
|
|
571
578
|
rb_sys_fail("IO_Event_Selector_KQueue_io_wait:IO_Event_Selector_KQueue_Waiting_register");
|
|
@@ -669,6 +676,8 @@ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
669
676
|
.offset = offset,
|
|
670
677
|
};
|
|
671
678
|
|
|
679
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
680
|
+
|
|
672
681
|
return rb_ensure(io_read_loop, (VALUE)&io_read_arguments, io_read_ensure, (VALUE)&io_read_arguments);
|
|
673
682
|
}
|
|
674
683
|
|
|
@@ -775,6 +784,8 @@ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
775
784
|
.offset = offset,
|
|
776
785
|
};
|
|
777
786
|
|
|
787
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
788
|
+
|
|
778
789
|
return rb_ensure(io_write_loop, (VALUE)&io_write_arguments, io_write_ensure, (VALUE)&io_write_arguments);
|
|
779
790
|
}
|
|
780
791
|
|
|
@@ -981,7 +992,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
|
981
992
|
// Non-comprehensive testing shows this gives a 1.5x speedup.
|
|
982
993
|
|
|
983
994
|
// First do the syscall with no timeout to get any immediately available events:
|
|
984
|
-
if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl timeout="
|
|
995
|
+
if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl timeout=" IO_EVENT_PRINTF_TIMESPEC "\r\n", IO_EVENT_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
|
|
985
996
|
select_internal_with_gvl(&arguments);
|
|
986
997
|
if (DEBUG) fprintf(stderr, "\r\nselect_internal_with_gvl done\r\n");
|
|
987
998
|
|
|
@@ -999,7 +1010,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
|
999
1010
|
struct timespec start_time;
|
|
1000
1011
|
IO_Event_Selector_current_time(&start_time);
|
|
1001
1012
|
|
|
1002
|
-
if (DEBUG) fprintf(stderr, "IO_Event_Selector_KQueue_select timeout="
|
|
1013
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_KQueue_select timeout=" IO_EVENT_PRINTF_TIMESPEC "\n", IO_EVENT_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
|
|
1003
1014
|
select_internal_without_gvl(&arguments);
|
|
1004
1015
|
|
|
1005
1016
|
struct timespec end_time;
|
|
@@ -1052,8 +1063,7 @@ VALUE IO_Event_Selector_KQueue_wakeup(VALUE self) {
|
|
|
1052
1063
|
}
|
|
1053
1064
|
|
|
1054
1065
|
void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
|
|
1055
|
-
IO_Event_Selector_KQueue = rb_define_class_under(IO_Event_Selector, "KQueue", rb_cObject);
|
|
1056
|
-
rb_gc_register_mark_object(IO_Event_Selector_KQueue);
|
|
1066
|
+
VALUE IO_Event_Selector_KQueue = rb_define_class_under(IO_Event_Selector, "KQueue", rb_cObject);
|
|
1057
1067
|
|
|
1058
1068
|
rb_define_alloc_func(IO_Event_Selector_KQueue, IO_Event_Selector_KQueue_allocate);
|
|
1059
1069
|
rb_define_method(IO_Event_Selector_KQueue, "initialize", IO_Event_Selector_KQueue_initialize, 1);
|
|
@@ -38,6 +38,7 @@ inline static void IO_Event_List_append(struct IO_Event_List *list, struct IO_Ev
|
|
|
38
38
|
head->tail = node;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Prepend an item to the beginning of the list.
|
|
41
42
|
inline static void IO_Event_List_prepend(struct IO_Event_List *list, struct IO_Event_List *node)
|
|
42
43
|
{
|
|
43
44
|
assert(node->head == NULL);
|
|
@@ -64,6 +65,7 @@ inline static void IO_Event_List_pop(struct IO_Event_List *node)
|
|
|
64
65
|
node->head = node->tail = NULL;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
// Remove an item from the list, if it is in a list.
|
|
67
69
|
inline static void IO_Event_List_free(struct IO_Event_List *node)
|
|
68
70
|
{
|
|
69
71
|
if (node->head && node->tail) {
|
|
@@ -71,11 +73,27 @@ inline static void IO_Event_List_free(struct IO_Event_List *node)
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
// Calculate the memory size of the list nodes.
|
|
77
|
+
inline static size_t IO_Event_List_memory_size(const struct IO_Event_List *list)
|
|
78
|
+
{
|
|
79
|
+
size_t memsize = 0;
|
|
80
|
+
|
|
81
|
+
const struct IO_Event_List *node = list->tail;
|
|
82
|
+
while (node != list) {
|
|
83
|
+
memsize += sizeof(struct IO_Event_List);
|
|
84
|
+
node = node->tail;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return memsize;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Return true if the list is empty.
|
|
91
|
+
inline static int IO_Event_List_empty(const struct IO_Event_List *list)
|
|
75
92
|
{
|
|
76
93
|
return list->head == list->tail;
|
|
77
94
|
}
|
|
78
95
|
|
|
96
|
+
// Enumerate all items in the list, assuming the list will not be modified during iteration.
|
|
79
97
|
inline static void IO_Event_List_immutable_each(struct IO_Event_List *list, void (*callback)(struct IO_Event_List *node))
|
|
80
98
|
{
|
|
81
99
|
struct IO_Event_List *node = list->tail;
|
|
@@ -172,24 +172,35 @@ static void queue_pop(struct IO_Event_Selector *backend, struct IO_Event_Selecto
|
|
|
172
172
|
if (waiting->head) {
|
|
173
173
|
waiting->head->tail = waiting->tail;
|
|
174
174
|
} else {
|
|
175
|
+
// We must have been at the head of the queue:
|
|
175
176
|
backend->waiting = waiting->tail;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
if (waiting->tail) {
|
|
179
180
|
waiting->tail->head = waiting->head;
|
|
180
181
|
} else {
|
|
182
|
+
// We must have been at the tail of the queue:
|
|
181
183
|
backend->ready = waiting->head;
|
|
182
184
|
}
|
|
185
|
+
|
|
186
|
+
waiting->head = NULL;
|
|
187
|
+
waiting->tail = NULL;
|
|
183
188
|
}
|
|
184
189
|
|
|
185
190
|
static void queue_push(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *waiting) {
|
|
191
|
+
assert(waiting->head == NULL);
|
|
192
|
+
assert(waiting->tail == NULL);
|
|
193
|
+
|
|
186
194
|
if (backend->waiting) {
|
|
195
|
+
// If there was an item in the queue already, we shift it along:
|
|
187
196
|
backend->waiting->head = waiting;
|
|
188
197
|
waiting->tail = backend->waiting;
|
|
189
198
|
} else {
|
|
199
|
+
// If the queue was empty, we update the tail too:
|
|
190
200
|
backend->ready = waiting;
|
|
191
201
|
}
|
|
192
202
|
|
|
203
|
+
// We always push to the front/head:
|
|
193
204
|
backend->waiting = waiting;
|
|
194
205
|
}
|
|
195
206
|
|
|
@@ -222,6 +233,8 @@ VALUE IO_Event_Selector_resume(struct IO_Event_Selector *backend, int argc, VALU
|
|
|
222
233
|
.fiber = rb_fiber_current()
|
|
223
234
|
};
|
|
224
235
|
|
|
236
|
+
RB_OBJ_WRITTEN(backend->self, Qundef, waiting.fiber);
|
|
237
|
+
|
|
225
238
|
queue_push(backend, &waiting);
|
|
226
239
|
|
|
227
240
|
struct wait_and_transfer_arguments arguments = {
|
|
@@ -255,6 +268,8 @@ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE
|
|
|
255
268
|
.fiber = rb_fiber_current()
|
|
256
269
|
};
|
|
257
270
|
|
|
271
|
+
RB_OBJ_WRITTEN(backend->self, Qundef, waiting.fiber);
|
|
272
|
+
|
|
258
273
|
queue_push(backend, &waiting);
|
|
259
274
|
|
|
260
275
|
struct wait_and_transfer_arguments arguments = {
|
|
@@ -270,11 +285,13 @@ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE
|
|
|
270
285
|
void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber)
|
|
271
286
|
{
|
|
272
287
|
struct IO_Event_Selector_Queue *waiting = malloc(sizeof(struct IO_Event_Selector_Queue));
|
|
288
|
+
assert(waiting);
|
|
273
289
|
|
|
274
290
|
waiting->head = NULL;
|
|
275
291
|
waiting->tail = NULL;
|
|
276
292
|
waiting->flags = IO_EVENT_SELECTOR_QUEUE_INTERNAL;
|
|
277
|
-
|
|
293
|
+
|
|
294
|
+
RB_OBJ_WRITE(backend->self, &waiting->fiber, fiber);
|
|
278
295
|
|
|
279
296
|
queue_push(backend, waiting);
|
|
280
297
|
}
|
|
@@ -283,9 +300,10 @@ static inline
|
|
|
283
300
|
void IO_Event_Selector_queue_pop(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *ready)
|
|
284
301
|
{
|
|
285
302
|
if (DEBUG) fprintf(stderr, "IO_Event_Selector_queue_pop -> %p\n", (void*)ready->fiber);
|
|
303
|
+
|
|
286
304
|
if (ready->flags & IO_EVENT_SELECTOR_QUEUE_FIBER) {
|
|
287
305
|
IO_Event_Selector_fiber_transfer(ready->fiber, 0, NULL);
|
|
288
|
-
} else {
|
|
306
|
+
} else if (ready->flags & IO_EVENT_SELECTOR_QUEUE_INTERNAL) {
|
|
289
307
|
VALUE fiber = ready->fiber;
|
|
290
308
|
queue_pop(backend, ready);
|
|
291
309
|
free(ready);
|
|
@@ -293,6 +311,8 @@ void IO_Event_Selector_queue_pop(struct IO_Event_Selector *backend, struct IO_Ev
|
|
|
293
311
|
if (RTEST(rb_funcall(fiber, id_alive_p, 0))) {
|
|
294
312
|
rb_funcall(fiber, id_transfer, 0);
|
|
295
313
|
}
|
|
314
|
+
} else {
|
|
315
|
+
rb_raise(rb_eRuntimeError, "Unknown queue type!");
|
|
296
316
|
}
|
|
297
317
|
}
|
|
298
318
|
|
|
@@ -300,6 +320,8 @@ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
|
|
|
300
320
|
{
|
|
301
321
|
int count = 0;
|
|
302
322
|
|
|
323
|
+
// During iteration of the queue, the same item may be re-queued. If we don't handle this correctly, we may end up in an infinite loop. So, to avoid this situation, we keep note of the current head of the queue and break the loop if we reach the same item again.
|
|
324
|
+
|
|
303
325
|
// Get the current tail and head of the queue:
|
|
304
326
|
struct IO_Event_Selector_Queue *waiting = backend->waiting;
|
|
305
327
|
if (DEBUG) fprintf(stderr, "IO_Event_Selector_queue_flush waiting = %p\n", waiting);
|
|
@@ -95,27 +95,29 @@ struct IO_Event_Selector_Queue {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
struct IO_Event_Selector {
|
|
98
|
+
VALUE self;
|
|
98
99
|
VALUE loop;
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Append to waiting.
|
|
101
|
+
// Append to waiting (front/head of queue).
|
|
103
102
|
struct IO_Event_Selector_Queue *waiting;
|
|
104
|
-
// Process from ready.
|
|
103
|
+
// Process from ready (back/tail of queue).
|
|
105
104
|
struct IO_Event_Selector_Queue *ready;
|
|
106
105
|
};
|
|
107
106
|
|
|
108
107
|
static inline
|
|
109
|
-
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE loop) {
|
|
110
|
-
backend->
|
|
108
|
+
void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop) {
|
|
109
|
+
RB_OBJ_WRITE(self, &backend->self, self);
|
|
110
|
+
RB_OBJ_WRITE(self, &backend->loop, loop);
|
|
111
111
|
backend->waiting = NULL;
|
|
112
112
|
backend->ready = NULL;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
static inline
|
|
116
116
|
void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
|
|
117
|
+
rb_gc_mark_movable(backend->self);
|
|
117
118
|
rb_gc_mark_movable(backend->loop);
|
|
118
119
|
|
|
120
|
+
// Walk backwards through the ready queue:
|
|
119
121
|
struct IO_Event_Selector_Queue *ready = backend->ready;
|
|
120
122
|
while (ready) {
|
|
121
123
|
rb_gc_mark_movable(ready->fiber);
|
|
@@ -125,6 +127,7 @@ void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
|
|
|
125
127
|
|
|
126
128
|
static inline
|
|
127
129
|
void IO_Event_Selector_compact(struct IO_Event_Selector *backend) {
|
|
130
|
+
backend->self = rb_gc_location(backend->self);
|
|
128
131
|
backend->loop = rb_gc_location(backend->loop);
|
|
129
132
|
|
|
130
133
|
struct IO_Event_Selector_Queue *ready = backend->ready;
|
|
@@ -149,5 +152,5 @@ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend);
|
|
|
149
152
|
void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration);
|
|
150
153
|
void IO_Event_Selector_current_time(struct timespec *time);
|
|
151
154
|
|
|
152
|
-
#define
|
|
153
|
-
#define
|
|
155
|
+
#define IO_EVENT_PRINTF_TIMESPEC "%lld.%.9ld"
|
|
156
|
+
#define IO_EVENT_PRINTF_TIMESPEC_ARGUMENTS(ts) (long long)((ts).tv_sec), (ts).tv_nsec
|