io-event 1.7.5 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -1
- data/ext/extconf.rb +1 -1
- data/ext/io/event/{selector/array.h → array.h} +7 -1
- data/ext/io/event/event.c +2 -19
- data/ext/io/event/event.h +2 -19
- data/ext/io/event/interrupt.c +12 -27
- data/ext/io/event/interrupt.h +2 -19
- data/ext/io/event/{selector/list.h → list.h} +1 -1
- data/ext/io/event/profile.c +154 -0
- data/ext/io/event/profile.h +54 -0
- data/ext/io/event/selector/epoll.c +13 -30
- data/ext/io/event/selector/epoll.h +2 -19
- data/ext/io/event/selector/kqueue.c +12 -29
- data/ext/io/event/selector/kqueue.h +2 -19
- data/ext/io/event/selector/pidfd.c +2 -19
- data/ext/io/event/selector/selector.c +65 -50
- data/ext/io/event/selector/selector.h +11 -28
- data/ext/io/event/selector/uring.c +12 -29
- data/ext/io/event/selector/uring.h +2 -19
- data/ext/io/event/time.c +28 -0
- data/ext/io/event/time.h +16 -0
- data/lib/io/event/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +26 -0
- data.tar.gz.sig +0 -0
- metadata +9 -10
- 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: ac3ad1ac294041242bb2f44d9c34bf6871f36b7ae01faaf49b83989b75058e84
|
4
|
+
data.tar.gz: e622ec2203010b41c260a763c97cac8025bf86d1d5b04b0ff916f3cf7d7eb467
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd0ba1be23b1c113af78c07d7d8aab84759d984ba1a0569cdfe31c427e34de7d97a8a7fc5598dd4cfe0ec650709c24bf764c1d5c385b8de34cf70157195dc4e7
|
7
|
+
data.tar.gz: 91936ec8d38c517cfdd6a22a72199067f49933811522eb71e4d23df49046725c1708f23481fb5f9430852143093594793f3635fd7a58624e539d97555eafb18a
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
ug���YՐ�2�����f�i:�I\b��L�Aua�:;�����n]���ܥ���y^�����}}1���Jge�t=܋�ES� qy��l�S�2`����.�H=��_���ەolZT��c��<�©
|
2
|
+
��fc�$��=BL}f�Ի����O���
|
data/ext/extconf.rb
CHANGED
@@ -22,7 +22,7 @@ if ENV.key?("RUBY_DEBUG")
|
|
22
22
|
$CFLAGS << " -DRUBY_DEBUG -O0"
|
23
23
|
end
|
24
24
|
|
25
|
-
$srcs = ["io/event/event.c", "io/event/selector/selector.c"]
|
25
|
+
$srcs = ["io/event/event.c", "io/event/selector/selector.c", "io/event/time.c", "io/event/profile.c"]
|
26
26
|
$VPATH << "$(srcdir)/io/event"
|
27
27
|
$VPATH << "$(srcdir)/io/event/selector"
|
28
28
|
|
@@ -28,7 +28,7 @@ struct IO_Event_Array {
|
|
28
28
|
void (*element_free)(void*);
|
29
29
|
};
|
30
30
|
|
31
|
-
inline static int
|
31
|
+
inline static int IO_Event_Array_initialize(struct IO_Event_Array *array, size_t count, size_t element_size)
|
32
32
|
{
|
33
33
|
array->limit = 0;
|
34
34
|
array->element_size = element_size;
|
@@ -153,6 +153,12 @@ inline static void* IO_Event_Array_lookup(struct IO_Event_Array *array, size_t i
|
|
153
153
|
return *element;
|
154
154
|
}
|
155
155
|
|
156
|
+
inline static void* IO_Event_Array_last(struct IO_Event_Array *array)
|
157
|
+
{
|
158
|
+
if (array->limit == 0) return NULL;
|
159
|
+
else return array->base[array->limit - 1];
|
160
|
+
}
|
161
|
+
|
156
162
|
// Push a new element onto the end of the array.
|
157
163
|
inline static void* IO_Event_Array_push(struct IO_Event_Array *array)
|
158
164
|
{
|
data/ext/io/event/event.c
CHANGED
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "event.h"
|
22
5
|
#include "selector/selector.h"
|
data/ext/io/event/event.h
CHANGED
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
data/ext/io/event/interrupt.c
CHANGED
@@ -1,24 +1,5 @@
|
|
1
|
-
//
|
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;
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
22
3
|
|
23
4
|
#include "interrupt.h"
|
24
5
|
#include <unistd.h>
|
@@ -95,9 +76,11 @@ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
|
|
95
76
|
ssize_t result = write(interrupt->descriptor[1], ".", 1);
|
96
77
|
|
97
78
|
if (result == -1) {
|
98
|
-
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
99
|
-
|
100
|
-
|
79
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
80
|
+
// If we can't write to the pipe, it means the other end is full. In that case, we can be sure that the other end has already been woken up or is about to be woken up.
|
81
|
+
} else {
|
82
|
+
rb_sys_fail("IO_Event_Interrupt_signal:write");
|
83
|
+
}
|
101
84
|
}
|
102
85
|
}
|
103
86
|
|
@@ -107,9 +90,11 @@ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
|
|
107
90
|
ssize_t result = read(interrupt->descriptor[0], buffer, sizeof(buffer));
|
108
91
|
|
109
92
|
if (result == -1) {
|
110
|
-
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
111
|
-
|
112
|
-
|
93
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
94
|
+
// If we can't read from the pipe, it means the other end is empty. In that case, we can be sure that the other end is already clear.
|
95
|
+
} else {
|
96
|
+
rb_sys_fail("IO_Event_Interrupt_clear:read");
|
97
|
+
}
|
113
98
|
}
|
114
99
|
}
|
115
100
|
#endif
|
data/ext/io/event/interrupt.h
CHANGED
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
@@ -0,0 +1,154 @@
|
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
3
|
+
|
4
|
+
#include "profile.h"
|
5
|
+
#include "time.h"
|
6
|
+
|
7
|
+
#include <ruby/debug.h>
|
8
|
+
|
9
|
+
#include <stdio.h>
|
10
|
+
|
11
|
+
void IO_Event_Profile_Event_initialize(struct IO_Event_Profile_Event *event) {
|
12
|
+
event->time.tv_sec = 0;
|
13
|
+
event->time.tv_nsec = 0;
|
14
|
+
event->nesting = 0;
|
15
|
+
|
16
|
+
event->event_flag = 0;
|
17
|
+
event->id = 0;
|
18
|
+
|
19
|
+
event->path = NULL;
|
20
|
+
event->line = 0;
|
21
|
+
}
|
22
|
+
|
23
|
+
void IO_Event_Profile_Event_free(struct IO_Event_Profile_Event *event) {
|
24
|
+
if (event->path) {
|
25
|
+
free((void*)event->path);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
static const char *event_flag_name(rb_event_flag_t event_flag) {
|
30
|
+
switch (event_flag) {
|
31
|
+
case RUBY_EVENT_LINE:
|
32
|
+
return "line";
|
33
|
+
case RUBY_EVENT_CALL:
|
34
|
+
case RUBY_EVENT_C_CALL:
|
35
|
+
return "call";
|
36
|
+
case RUBY_EVENT_RETURN:
|
37
|
+
case RUBY_EVENT_C_RETURN:
|
38
|
+
return "return";
|
39
|
+
default:
|
40
|
+
return "unknown";
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
int event_flag_call_p(rb_event_flag_t event_flags) {
|
45
|
+
return event_flags & (RUBY_EVENT_CALL | RUBY_EVENT_C_CALL);
|
46
|
+
}
|
47
|
+
|
48
|
+
int event_flag_return_p(rb_event_flag_t event_flags) {
|
49
|
+
return event_flags & (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN);
|
50
|
+
}
|
51
|
+
|
52
|
+
static void profile_event_callback(rb_event_flag_t event_flag, VALUE data, VALUE self, ID id, VALUE klass) {
|
53
|
+
struct IO_Event_Profile *profile = (struct IO_Event_Profile*)data;
|
54
|
+
struct IO_Event_Profile_Event *event = IO_Event_Array_push(&profile->events);
|
55
|
+
|
56
|
+
IO_Event_Time_current(&event->time);
|
57
|
+
|
58
|
+
event->event_flag = event_flag;
|
59
|
+
|
60
|
+
if (event_flag_call_p(event_flag)) {
|
61
|
+
event->parent = profile->current;
|
62
|
+
profile->current = event;
|
63
|
+
|
64
|
+
event->nesting = profile->nesting;
|
65
|
+
profile->nesting += 1;
|
66
|
+
|
67
|
+
if (id) {
|
68
|
+
event->id = id;
|
69
|
+
event->klass = klass;
|
70
|
+
} else {
|
71
|
+
rb_frame_method_id_and_class(&event->id, &event->klass);
|
72
|
+
}
|
73
|
+
|
74
|
+
const char *path = rb_sourcefile();
|
75
|
+
if (path) {
|
76
|
+
event->path = strdup(path);
|
77
|
+
}
|
78
|
+
event->line = rb_sourceline();
|
79
|
+
} else if (event_flag_return_p(event_flag)) {
|
80
|
+
// Set up the call/return pair:
|
81
|
+
profile->current->pair = event;
|
82
|
+
event->pair = profile->current;
|
83
|
+
|
84
|
+
profile->current = profile->current->parent;
|
85
|
+
event->parent = profile->current;
|
86
|
+
|
87
|
+
profile->nesting -= 1;
|
88
|
+
event->nesting = profile->nesting;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
void IO_Event_Profile_initialize(struct IO_Event_Profile *profile, VALUE fiber) {
|
93
|
+
profile->fiber = fiber;
|
94
|
+
|
95
|
+
profile->events.element_initialize = (void (*)(void*))IO_Event_Profile_Event_initialize;
|
96
|
+
profile->events.element_free = (void (*)(void*))IO_Event_Profile_Event_free;
|
97
|
+
|
98
|
+
IO_Event_Array_initialize(&profile->events, 0, sizeof(struct IO_Event_Profile_Event));
|
99
|
+
}
|
100
|
+
|
101
|
+
void IO_Event_Profile_start(struct IO_Event_Profile *profile) {
|
102
|
+
IO_Event_Time_current(&profile->start_time);
|
103
|
+
profile->nesting = 0;
|
104
|
+
profile->current = NULL;
|
105
|
+
|
106
|
+
// Since fibers are currently limited to a single thread, we use this in the hope that it's a little more efficient:
|
107
|
+
VALUE thread = rb_thread_current();
|
108
|
+
rb_thread_add_event_hook(thread, profile_event_callback, RUBY_EVENT_CALL | RUBY_EVENT_C_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN, (VALUE)profile);
|
109
|
+
}
|
110
|
+
|
111
|
+
void IO_Event_Profile_stop(struct IO_Event_Profile *profile) {
|
112
|
+
IO_Event_Time_current(&profile->stop_time);
|
113
|
+
|
114
|
+
VALUE thread = rb_thread_current();
|
115
|
+
rb_thread_remove_event_hook_with_data(thread, profile_event_callback, (VALUE)profile);
|
116
|
+
}
|
117
|
+
|
118
|
+
void IO_Event_Profile_free(struct IO_Event_Profile *profile) {
|
119
|
+
IO_Event_Array_free(&profile->events);
|
120
|
+
}
|
121
|
+
|
122
|
+
static const float IO_EVENT_PROFILE_PRINT_MINIMUM_PROPORTION = 0.01;
|
123
|
+
|
124
|
+
void IO_Event_Profile_print(FILE *restrict stream, struct IO_Event_Profile *profile) {
|
125
|
+
struct timespec total_duration = {};
|
126
|
+
IO_Event_Time_elapsed(&profile->start_time, &profile->stop_time, &total_duration);
|
127
|
+
|
128
|
+
size_t skipped = 0;
|
129
|
+
|
130
|
+
for (size_t i = 0; i < profile->events.limit; i += 1) {
|
131
|
+
struct IO_Event_Profile_Event *event = profile->events.base[i];
|
132
|
+
|
133
|
+
if (event_flag_call_p(event->event_flag)) {
|
134
|
+
struct timespec duration = {};
|
135
|
+
|
136
|
+
if (event->pair) {
|
137
|
+
IO_Event_Time_elapsed(&event->time, &event->pair->time, &duration);
|
138
|
+
|
139
|
+
// Skip events that are too short to be meaningful:
|
140
|
+
if (IO_Event_Time_proportion(&duration, &total_duration) < IO_EVENT_PROFILE_PRINT_MINIMUM_PROPORTION) {
|
141
|
+
skipped += 1;
|
142
|
+
continue;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
for (size_t i = 0; i < event->nesting; i += 1) {
|
147
|
+
fputc('\t', stream);
|
148
|
+
}
|
149
|
+
|
150
|
+
const char *name = rb_id2name(event->id);
|
151
|
+
fprintf(stream, "\t%s:%d in '%s#%s' (" IO_EVENT_TIME_PRINTF_TIMESPEC "s)\n", event->path, event->line, RSTRING_PTR(rb_inspect(event->klass)), name, IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(duration));
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
3
|
+
|
4
|
+
#pragma once
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
#include "array.h"
|
8
|
+
#include "time.h"
|
9
|
+
|
10
|
+
struct IO_Event_Profile_Event {
|
11
|
+
struct timespec time;
|
12
|
+
size_t nesting;
|
13
|
+
|
14
|
+
rb_event_flag_t event_flag;
|
15
|
+
ID id;
|
16
|
+
|
17
|
+
VALUE klass;
|
18
|
+
const char *path;
|
19
|
+
int line;
|
20
|
+
|
21
|
+
struct IO_Event_Profile_Event *parent;
|
22
|
+
struct IO_Event_Profile_Event *pair;
|
23
|
+
};
|
24
|
+
|
25
|
+
struct IO_Event_Profile {
|
26
|
+
VALUE fiber;
|
27
|
+
|
28
|
+
struct timespec start_time;
|
29
|
+
struct timespec stop_time;
|
30
|
+
|
31
|
+
// The depth of the call stack:
|
32
|
+
size_t nesting;
|
33
|
+
|
34
|
+
// The current call frame:
|
35
|
+
struct IO_Event_Profile_Event *current;
|
36
|
+
|
37
|
+
struct IO_Event_Array events;
|
38
|
+
};
|
39
|
+
|
40
|
+
void IO_Event_Profile_initialize(struct IO_Event_Profile *profile, VALUE fiber);
|
41
|
+
|
42
|
+
void IO_Event_Profile_start(struct IO_Event_Profile *profile);
|
43
|
+
void IO_Event_Profile_stop(struct IO_Event_Profile *profile);
|
44
|
+
|
45
|
+
void IO_Event_Profile_free(struct IO_Event_Profile *profile);
|
46
|
+
void IO_Event_Profile_print(FILE *restrict stream, struct IO_Event_Profile *profile);
|
47
|
+
|
48
|
+
static inline float IO_Event_Profile_duration(struct IO_Event_Profile *profile) {
|
49
|
+
struct timespec duration;
|
50
|
+
|
51
|
+
IO_Event_Time_elapsed(&profile->start_time, &profile->stop_time, &duration);
|
52
|
+
|
53
|
+
return IO_Event_Time_duration(&duration);
|
54
|
+
}
|
@@ -1,27 +1,10 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "epoll.h"
|
22
5
|
#include "selector.h"
|
23
|
-
#include "list.h"
|
24
|
-
#include "array.h"
|
6
|
+
#include "../list.h"
|
7
|
+
#include "../array.h"
|
25
8
|
|
26
9
|
#include <sys/epoll.h>
|
27
10
|
#include <time.h>
|
@@ -337,9 +320,9 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
|
|
337
320
|
|
338
321
|
selector->descriptors.element_initialize = IO_Event_Selector_EPoll_Descriptor_initialize;
|
339
322
|
selector->descriptors.element_free = IO_Event_Selector_EPoll_Descriptor_free;
|
340
|
-
int result =
|
323
|
+
int result = IO_Event_Array_initialize(&selector->descriptors, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_EPoll_Descriptor));
|
341
324
|
if (result < 0) {
|
342
|
-
rb_sys_fail("IO_Event_Selector_EPoll_allocate:
|
325
|
+
rb_sys_fail("IO_Event_Selector_EPoll_allocate:IO_Event_Array_initialize");
|
343
326
|
}
|
344
327
|
|
345
328
|
return instance;
|
@@ -435,7 +418,7 @@ VALUE IO_Event_Selector_EPoll_push(VALUE self, VALUE fiber)
|
|
435
418
|
struct IO_Event_Selector_EPoll *selector = NULL;
|
436
419
|
TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
|
437
420
|
|
438
|
-
|
421
|
+
IO_Event_Selector_ready_push(&selector->backend, fiber);
|
439
422
|
|
440
423
|
return Qnil;
|
441
424
|
}
|
@@ -584,7 +567,7 @@ VALUE IO_Event_Selector_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
|
|
584
567
|
|
585
568
|
if (result == -1) {
|
586
569
|
if (errno == EPERM) {
|
587
|
-
|
570
|
+
IO_Event_Selector_ready_push(&selector->backend, fiber);
|
588
571
|
IO_Event_Selector_yield(&selector->backend);
|
589
572
|
return events;
|
590
573
|
}
|
@@ -942,7 +925,7 @@ int IO_Event_Selector_EPoll_handle(struct IO_Event_Selector_EPoll *selector, con
|
|
942
925
|
|
943
926
|
// Resume the fiber:
|
944
927
|
waiting->ready = matching_events;
|
945
|
-
|
928
|
+
IO_Event_Selector_fiber_transfer_user(waiting->fiber, 0, NULL);
|
946
929
|
|
947
930
|
node = saved->tail;
|
948
931
|
IO_Event_List_pop(saved);
|
@@ -994,7 +977,7 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
|
|
994
977
|
selector->idle_duration.tv_sec = 0;
|
995
978
|
selector->idle_duration.tv_nsec = 0;
|
996
979
|
|
997
|
-
int ready =
|
980
|
+
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
998
981
|
|
999
982
|
struct select_arguments arguments = {
|
1000
983
|
.selector = selector,
|
@@ -1020,14 +1003,14 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
|
|
1020
1003
|
|
1021
1004
|
if (!timeout_nonblocking(arguments.timeout)) {
|
1022
1005
|
struct timespec start_time;
|
1023
|
-
|
1006
|
+
IO_Event_Time_current(&start_time);
|
1024
1007
|
|
1025
1008
|
// Wait for events to occur:
|
1026
1009
|
select_internal_without_gvl(&arguments);
|
1027
1010
|
|
1028
1011
|
struct timespec end_time;
|
1029
|
-
|
1030
|
-
|
1012
|
+
IO_Event_Time_current(&end_time);
|
1013
|
+
IO_Event_Time_elapsed(&start_time, &end_time, &selector->idle_duration);
|
1031
1014
|
}
|
1032
1015
|
}
|
1033
1016
|
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
@@ -1,27 +1,10 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "kqueue.h"
|
22
5
|
#include "selector.h"
|
23
|
-
#include "list.h"
|
24
|
-
#include "array.h"
|
6
|
+
#include "../list.h"
|
7
|
+
#include "../array.h"
|
25
8
|
|
26
9
|
#include <sys/event.h>
|
27
10
|
#include <sys/ioctl.h>
|
@@ -312,9 +295,9 @@ VALUE IO_Event_Selector_KQueue_allocate(VALUE self) {
|
|
312
295
|
selector->descriptors.element_initialize = IO_Event_Selector_KQueue_Descriptor_initialize;
|
313
296
|
selector->descriptors.element_free = IO_Event_Selector_KQueue_Descriptor_free;
|
314
297
|
|
315
|
-
int result =
|
298
|
+
int result = IO_Event_Array_initialize(&selector->descriptors, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_KQueue_Descriptor));
|
316
299
|
if (result < 0) {
|
317
|
-
rb_sys_fail("IO_Event_Selector_KQueue_allocate:
|
300
|
+
rb_sys_fail("IO_Event_Selector_KQueue_allocate:IO_Event_Array_initialize");
|
318
301
|
}
|
319
302
|
|
320
303
|
return instance;
|
@@ -422,7 +405,7 @@ VALUE IO_Event_Selector_KQueue_push(VALUE self, VALUE fiber)
|
|
422
405
|
struct IO_Event_Selector_KQueue *selector = NULL;
|
423
406
|
TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
|
424
407
|
|
425
|
-
|
408
|
+
IO_Event_Selector_ready_push(&selector->backend, fiber);
|
426
409
|
|
427
410
|
return Qnil;
|
428
411
|
}
|
@@ -914,7 +897,7 @@ int IO_Event_Selector_KQueue_handle(struct IO_Event_Selector_KQueue *selector, u
|
|
914
897
|
IO_Event_List_append(node, saved);
|
915
898
|
|
916
899
|
waiting->ready = matching_events;
|
917
|
-
|
900
|
+
IO_Event_Selector_fiber_transfer_user(waiting->fiber, 0, NULL);
|
918
901
|
|
919
902
|
node = saved->tail;
|
920
903
|
IO_Event_List_pop(saved);
|
@@ -971,7 +954,7 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
971
954
|
selector->idle_duration.tv_sec = 0;
|
972
955
|
selector->idle_duration.tv_nsec = 0;
|
973
956
|
|
974
|
-
int ready =
|
957
|
+
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
975
958
|
|
976
959
|
struct select_arguments arguments = {
|
977
960
|
.selector = selector,
|
@@ -1008,14 +991,14 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
|
|
1008
991
|
arguments.count = KQUEUE_MAX_EVENTS;
|
1009
992
|
|
1010
993
|
struct timespec start_time;
|
1011
|
-
|
994
|
+
IO_Event_Time_current(&start_time);
|
1012
995
|
|
1013
996
|
if (DEBUG) fprintf(stderr, "IO_Event_Selector_KQueue_select timeout=" IO_EVENT_PRINTF_TIMESPEC "\n", IO_EVENT_PRINTF_TIMESPEC_ARGUMENTS(arguments.storage));
|
1014
997
|
select_internal_without_gvl(&arguments);
|
1015
998
|
|
1016
999
|
struct timespec end_time;
|
1017
|
-
|
1018
|
-
|
1000
|
+
IO_Event_Time_current(&end_time);
|
1001
|
+
IO_Event_Time_elapsed(&start_time, &end_time, &selector->idle_duration);
|
1019
1002
|
}
|
1020
1003
|
}
|
1021
1004
|
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include <ruby.h>
|
22
5
|
|
@@ -1,30 +1,18 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "selector.h"
|
5
|
+
#include "../profile.h"
|
6
|
+
|
22
7
|
#include <fcntl.h>
|
8
|
+
#include <stdlib.h>
|
23
9
|
|
24
10
|
static const int DEBUG = 0;
|
25
11
|
|
26
12
|
static ID id_transfer, id_alive_p;
|
27
13
|
|
14
|
+
static float IO_Event_Selector_stall_log_threshold = 0;
|
15
|
+
|
28
16
|
VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv) {
|
29
17
|
// TODO Consider introducing something like `rb_fiber_scheduler_transfer(...)`.
|
30
18
|
#ifdef HAVE__RB_FIBER_TRANSFER
|
@@ -43,6 +31,31 @@ VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv) {
|
|
43
31
|
return Qnil;
|
44
32
|
}
|
45
33
|
|
34
|
+
VALUE IO_Event_Selector_fiber_transfer_user(VALUE fiber, int argc, VALUE *argv) {
|
35
|
+
// Bypass if the threshold is not set:
|
36
|
+
if (IO_Event_Selector_stall_log_threshold == 0) {
|
37
|
+
return IO_Event_Selector_fiber_transfer(fiber, argc, argv);
|
38
|
+
}
|
39
|
+
|
40
|
+
struct IO_Event_Profile profile;
|
41
|
+
IO_Event_Profile_initialize(&profile, fiber);
|
42
|
+
IO_Event_Profile_start(&profile);
|
43
|
+
|
44
|
+
// Transfer control to the fiber:
|
45
|
+
VALUE result = IO_Event_Selector_fiber_transfer(fiber, argc, argv);
|
46
|
+
|
47
|
+
IO_Event_Profile_stop(&profile);
|
48
|
+
|
49
|
+
float duration = IO_Event_Profile_duration(&profile);
|
50
|
+
|
51
|
+
if (duration > IO_Event_Selector_stall_log_threshold) {
|
52
|
+
fprintf(stderr, "Fiber stalled for %.3f seconds\n", duration);
|
53
|
+
IO_Event_Profile_print(stderr, &profile);
|
54
|
+
}
|
55
|
+
|
56
|
+
return result;
|
57
|
+
}
|
58
|
+
|
46
59
|
#ifndef HAVE__RB_FIBER_RAISE
|
47
60
|
static ID id_raise;
|
48
61
|
|
@@ -156,8 +169,25 @@ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
156
169
|
rb_Process_Status = rb_const_get_at(rb_mProcess, rb_intern("Status"));
|
157
170
|
rb_gc_register_mark_object(rb_Process_Status);
|
158
171
|
#endif
|
159
|
-
|
172
|
+
|
160
173
|
rb_define_singleton_method(IO_Event_Selector, "nonblock", IO_Event_Selector_nonblock, 1);
|
174
|
+
|
175
|
+
// Extract the stall log threshold if specified:
|
176
|
+
|
177
|
+
char *stall_log_threshold = getenv("IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD");
|
178
|
+
// Can be true, false or a floating point time in seconds:
|
179
|
+
|
180
|
+
if (stall_log_threshold) {
|
181
|
+
if (strcmp(stall_log_threshold, "true") == 0) {
|
182
|
+
IO_Event_Selector_stall_log_threshold = 0.001;
|
183
|
+
} else if (strcmp(stall_log_threshold, "false") == 0) {
|
184
|
+
IO_Event_Selector_stall_log_threshold = 0;
|
185
|
+
} else {
|
186
|
+
IO_Event_Selector_stall_log_threshold = strtof(stall_log_threshold, NULL);
|
187
|
+
}
|
188
|
+
|
189
|
+
if (DEBUG) fprintf(stderr, "IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD = %.3f\n", IO_Event_Selector_stall_log_threshold);
|
190
|
+
}
|
161
191
|
}
|
162
192
|
|
163
193
|
struct wait_and_transfer_arguments {
|
@@ -211,7 +241,7 @@ static VALUE wait_and_transfer(VALUE _arguments) {
|
|
211
241
|
int argc = arguments->argc - 1;
|
212
242
|
VALUE *argv = arguments->argv + 1;
|
213
243
|
|
214
|
-
return
|
244
|
+
return IO_Event_Selector_fiber_transfer_user(fiber, argc, argv);
|
215
245
|
}
|
216
246
|
|
217
247
|
static VALUE wait_and_transfer_ensure(VALUE _arguments) {
|
@@ -282,7 +312,7 @@ VALUE IO_Event_Selector_raise(struct IO_Event_Selector *backend, int argc, VALUE
|
|
282
312
|
return rb_ensure(wait_and_raise, (VALUE)&arguments, wait_and_transfer_ensure, (VALUE)&arguments);
|
283
313
|
}
|
284
314
|
|
285
|
-
void
|
315
|
+
void IO_Event_Selector_ready_push(struct IO_Event_Selector *backend, VALUE fiber)
|
286
316
|
{
|
287
317
|
struct IO_Event_Selector_Queue *waiting = malloc(sizeof(struct IO_Event_Selector_Queue));
|
288
318
|
assert(waiting);
|
@@ -297,26 +327,26 @@ void IO_Event_Selector_queue_push(struct IO_Event_Selector *backend, VALUE fiber
|
|
297
327
|
}
|
298
328
|
|
299
329
|
static inline
|
300
|
-
void
|
330
|
+
void IO_Event_Selector_ready_pop(struct IO_Event_Selector *backend, struct IO_Event_Selector_Queue *ready)
|
301
331
|
{
|
302
|
-
if (DEBUG) fprintf(stderr, "
|
332
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_ready_pop -> %p\n", (void*)ready->fiber);
|
303
333
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
334
|
+
VALUE fiber = ready->fiber;
|
335
|
+
|
336
|
+
if (ready->flags & IO_EVENT_SELECTOR_QUEUE_INTERNAL) {
|
337
|
+
// This means that the fiber was added to the ready queue by the selector itself, and we need to transfer control to it, but before we do that, we need to remove it from the queue, as there is no expectation that returning from `transfer` will remove it.
|
308
338
|
queue_pop(backend, ready);
|
309
339
|
free(ready);
|
310
|
-
|
311
|
-
|
312
|
-
rb_funcall(fiber, id_transfer, 0);
|
313
|
-
}
|
340
|
+
} else if (ready->flags & IO_EVENT_SELECTOR_QUEUE_FIBER) {
|
341
|
+
// This means the fiber added itself to the ready queue, and we need to transfer control back to it. Transferring control back to the fiber will call `queue_pop` and remove it from the queue.
|
314
342
|
} else {
|
315
343
|
rb_raise(rb_eRuntimeError, "Unknown queue type!");
|
316
344
|
}
|
345
|
+
|
346
|
+
IO_Event_Selector_fiber_transfer_user(fiber, 0, NULL);
|
317
347
|
}
|
318
348
|
|
319
|
-
int
|
349
|
+
int IO_Event_Selector_ready_flush(struct IO_Event_Selector *backend)
|
320
350
|
{
|
321
351
|
int count = 0;
|
322
352
|
|
@@ -324,7 +354,7 @@ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
|
|
324
354
|
|
325
355
|
// Get the current tail and head of the queue:
|
326
356
|
struct IO_Event_Selector_Queue *waiting = backend->waiting;
|
327
|
-
if (DEBUG) fprintf(stderr, "
|
357
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_ready_flush waiting = %p\n", waiting);
|
328
358
|
|
329
359
|
// Process from head to tail in order:
|
330
360
|
// During this, more items may be appended to tail.
|
@@ -333,25 +363,10 @@ int IO_Event_Selector_queue_flush(struct IO_Event_Selector *backend)
|
|
333
363
|
struct IO_Event_Selector_Queue *ready = backend->ready;
|
334
364
|
|
335
365
|
count += 1;
|
336
|
-
|
366
|
+
IO_Event_Selector_ready_pop(backend, ready);
|
337
367
|
|
338
368
|
if (ready == waiting) break;
|
339
369
|
}
|
340
370
|
|
341
371
|
return count;
|
342
372
|
}
|
343
|
-
|
344
|
-
void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration)
|
345
|
-
{
|
346
|
-
if ((stop->tv_nsec - start->tv_nsec) < 0) {
|
347
|
-
duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
|
348
|
-
duration->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
|
349
|
-
} else {
|
350
|
-
duration->tv_sec = stop->tv_sec - start->tv_sec;
|
351
|
-
duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
void IO_Event_Selector_current_time(struct timespec *time) {
|
356
|
-
clock_gettime(CLOCK_MONOTONIC, time);
|
357
|
-
}
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
@@ -24,6 +7,8 @@
|
|
24
7
|
#include <ruby/thread.h>
|
25
8
|
#include <ruby/io.h>
|
26
9
|
|
10
|
+
#include "../time.h"
|
11
|
+
|
27
12
|
#ifdef HAVE_RUBY_IO_BUFFER_H
|
28
13
|
#include <ruby/io/buffer.h>
|
29
14
|
#include <ruby/fiber/scheduler.h>
|
@@ -33,8 +18,6 @@
|
|
33
18
|
#define RUBY_FIBER_SCHEDULER_VERSION 1
|
34
19
|
#endif
|
35
20
|
|
36
|
-
#include <time.h>
|
37
|
-
|
38
21
|
#ifdef HAVE_SYS_WAIT_H
|
39
22
|
#include <sys/wait.h>
|
40
23
|
#endif
|
@@ -58,6 +41,9 @@ static inline int IO_Event_try_again(int error) {
|
|
58
41
|
|
59
42
|
VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv);
|
60
43
|
|
44
|
+
// Specifically for transferring control to a user fiber.
|
45
|
+
VALUE IO_Event_Selector_fiber_transfer_user(VALUE fiber, int argc, VALUE *argv);
|
46
|
+
|
61
47
|
#ifdef HAVE__RB_FIBER_RAISE
|
62
48
|
#define IO_Event_Selector_fiber_raise(fiber, argc, argv) rb_fiber_raise(fiber, argc, argv)
|
63
49
|
#else
|
@@ -146,11 +132,8 @@ VALUE IO_Event_Selector_yield(struct IO_Event_Selector *backend)
|
|
146
132
|
return IO_Event_Selector_resume(backend, 1, &backend->loop);
|
147
133
|
}
|
148
134
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
void IO_Event_Selector_elapsed_time(struct timespec* start, struct timespec* stop, struct timespec *duration);
|
153
|
-
void IO_Event_Selector_current_time(struct timespec *time);
|
135
|
+
// Append to the ready queue.
|
136
|
+
void IO_Event_Selector_ready_push(struct IO_Event_Selector *backend, VALUE fiber);
|
154
137
|
|
155
|
-
|
156
|
-
|
138
|
+
// Flush the ready queue by transferring control one at a time.
|
139
|
+
int IO_Event_Selector_ready_flush(struct IO_Event_Selector *backend);
|
@@ -1,27 +1,10 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#include "uring.h"
|
22
5
|
#include "selector.h"
|
23
|
-
#include "list.h"
|
24
|
-
#include "array.h"
|
6
|
+
#include "../list.h"
|
7
|
+
#include "../array.h"
|
25
8
|
|
26
9
|
#include <liburing.h>
|
27
10
|
#include <poll.h>
|
@@ -238,9 +221,9 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
238
221
|
|
239
222
|
selector->completions.element_initialize = IO_Event_Selector_URing_Completion_initialize;
|
240
223
|
selector->completions.element_free = IO_Event_Selector_URing_Completion_free;
|
241
|
-
int result =
|
224
|
+
int result = IO_Event_Array_initialize(&selector->completions, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_URing_Completion));
|
242
225
|
if (result < 0) {
|
243
|
-
rb_sys_fail("IO_Event_Selector_URing_allocate:
|
226
|
+
rb_sys_fail("IO_Event_Selector_URing_allocate:IO_Event_Array_initialize");
|
244
227
|
}
|
245
228
|
|
246
229
|
return instance;
|
@@ -318,7 +301,7 @@ VALUE IO_Event_Selector_URing_push(VALUE self, VALUE fiber)
|
|
318
301
|
struct IO_Event_Selector_URing *selector = NULL;
|
319
302
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
320
303
|
|
321
|
-
|
304
|
+
IO_Event_Selector_ready_push(&selector->backend, fiber);
|
322
305
|
|
323
306
|
return Qnil;
|
324
307
|
}
|
@@ -1103,7 +1086,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
1103
1086
|
if (waiting && waiting->fiber) {
|
1104
1087
|
assert(waiting->result != -ECANCELED);
|
1105
1088
|
|
1106
|
-
|
1089
|
+
IO_Event_Selector_fiber_transfer_user(waiting->fiber, 0, NULL);
|
1107
1090
|
}
|
1108
1091
|
}
|
1109
1092
|
|
@@ -1122,7 +1105,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
1122
1105
|
// Flush any pending events:
|
1123
1106
|
io_uring_submit_flush(selector);
|
1124
1107
|
|
1125
|
-
int ready =
|
1108
|
+
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
1126
1109
|
|
1127
1110
|
int result = select_process_completions(selector);
|
1128
1111
|
|
@@ -1142,14 +1125,14 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
1142
1125
|
|
1143
1126
|
if (!selector->backend.ready && !timeout_nonblocking(arguments.timeout)) {
|
1144
1127
|
struct timespec start_time;
|
1145
|
-
|
1128
|
+
IO_Event_Time_current(&start_time);
|
1146
1129
|
|
1147
1130
|
// This is a blocking operation, we wait for events:
|
1148
1131
|
result = select_internal_without_gvl(&arguments);
|
1149
1132
|
|
1150
1133
|
struct timespec end_time;
|
1151
|
-
|
1152
|
-
|
1134
|
+
IO_Event_Time_current(&end_time);
|
1135
|
+
IO_Event_Time_elapsed(&start_time, &end_time, &selector->idle_duration);
|
1153
1136
|
|
1154
1137
|
// After waiting/flushing the SQ, check if there are any completions:
|
1155
1138
|
if (result > 0) {
|
@@ -1,22 +1,5 @@
|
|
1
|
-
//
|
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.
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
20
3
|
|
21
4
|
#pragma once
|
22
5
|
|
data/ext/io/event/time.c
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
3
|
+
|
4
|
+
#include "time.h"
|
5
|
+
|
6
|
+
void IO_Event_Time_elapsed(struct timespec* start, struct timespec* stop, struct timespec *duration)
|
7
|
+
{
|
8
|
+
if ((stop->tv_nsec - start->tv_nsec) < 0) {
|
9
|
+
duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
|
10
|
+
duration->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
|
11
|
+
} else {
|
12
|
+
duration->tv_sec = stop->tv_sec - start->tv_sec;
|
13
|
+
duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
float IO_Event_Time_duration(struct timespec *duration)
|
18
|
+
{
|
19
|
+
return duration->tv_sec + duration->tv_nsec / 1000000000.0;
|
20
|
+
}
|
21
|
+
|
22
|
+
void IO_Event_Time_current(struct timespec *time) {
|
23
|
+
clock_gettime(CLOCK_MONOTONIC, time);
|
24
|
+
}
|
25
|
+
|
26
|
+
float IO_Event_Time_proportion(struct timespec *duration, struct timespec *total_duration) {
|
27
|
+
return IO_Event_Time_duration(duration) / IO_Event_Time_duration(total_duration);
|
28
|
+
}
|
data/ext/io/event/time.h
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
// Released under the MIT License.
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
3
|
+
|
4
|
+
#pragma once
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
#include <time.h>
|
8
|
+
|
9
|
+
void IO_Event_Time_elapsed(struct timespec* start, struct timespec* stop, struct timespec *duration);
|
10
|
+
float IO_Event_Time_duration(struct timespec *duration);
|
11
|
+
void IO_Event_Time_current(struct timespec *time);
|
12
|
+
|
13
|
+
float IO_Event_Time_proportion(struct timespec *duration, struct timespec *total_duration);
|
14
|
+
|
15
|
+
#define IO_EVENT_TIME_PRINTF_TIMESPEC "%.3g"
|
16
|
+
#define IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(ts) ((double)(ts).tv_sec + (ts).tv_nsec / 1e9)
|
data/lib/io/event/version.rb
CHANGED
data/readme.md
CHANGED
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
|
|
18
18
|
|
19
19
|
Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
|
20
20
|
|
21
|
+
### v1.8.0
|
22
|
+
|
23
|
+
- [Detecing fibers that are stalling the event loop.](https://socketry.github.io/io-event/releases/index#detecing-fibers-that-are-stalling-the-event-loop.)
|
24
|
+
|
21
25
|
### v1.7.5
|
22
26
|
|
23
27
|
- Fix `process_wait` race condition on EPoll that could cause a hang.
|
data/releases.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v1.8.0
|
4
|
+
|
5
|
+
### Detecing fibers that are stalling the event loop.
|
6
|
+
|
7
|
+
A new (experimental) feature for detecting fiber stalls has been added. This feature is disabled by default and can be enabled by setting the `IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD` to `true` or a floating point number representing the threshold in seconds.
|
8
|
+
|
9
|
+
When enabled, the event loop will measure and profile user code when resuming a fiber. If the fiber takes too long to return back to the event loop, the event loop will log a warning message with a profile of the fiber's execution.
|
10
|
+
|
11
|
+
> cat test.rb
|
12
|
+
#!/usr/bin/env ruby
|
13
|
+
|
14
|
+
require_relative "lib/async"
|
15
|
+
|
16
|
+
Async do
|
17
|
+
Fiber.blocking do
|
18
|
+
sleep 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
> IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD=true bundle exec ./test.rb
|
23
|
+
Fiber stalled for 1.003 seconds
|
24
|
+
/home/samuel/Developer/socketry/async/test.rb:6 in '#<Class:Fiber>#blocking' (1s)
|
25
|
+
/home/samuel/Developer/socketry/async/test.rb:7 in 'Kernel#sleep' (1s)
|
26
|
+
|
27
|
+
There is a performance overhead to this feature, so it is recommended to only enable it when debugging performance issues.
|
28
|
+
|
3
29
|
## v1.7.5
|
4
30
|
|
5
31
|
- Fix `process_wait` race condition on EPoll that could cause a hang.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-event
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -13,7 +13,6 @@ authors:
|
|
13
13
|
- Anthony Ross
|
14
14
|
- Delton Ding
|
15
15
|
- Pavel Rosický
|
16
|
-
autorequire:
|
17
16
|
bindir: bin
|
18
17
|
cert_chain:
|
19
18
|
- |
|
@@ -45,10 +44,8 @@ cert_chain:
|
|
45
44
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
46
45
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
47
46
|
-----END CERTIFICATE-----
|
48
|
-
date:
|
47
|
+
date: 2025-02-05 00:00:00.000000000 Z
|
49
48
|
dependencies: []
|
50
|
-
description:
|
51
|
-
email:
|
52
49
|
executables: []
|
53
50
|
extensions:
|
54
51
|
- ext/extconf.rb
|
@@ -56,21 +53,25 @@ extra_rdoc_files: []
|
|
56
53
|
files:
|
57
54
|
- design.md
|
58
55
|
- ext/extconf.rb
|
56
|
+
- ext/io/event/array.h
|
59
57
|
- ext/io/event/event.c
|
60
58
|
- ext/io/event/event.h
|
61
59
|
- ext/io/event/interrupt.c
|
62
60
|
- ext/io/event/interrupt.h
|
63
|
-
- ext/io/event/
|
61
|
+
- ext/io/event/list.h
|
62
|
+
- ext/io/event/profile.c
|
63
|
+
- ext/io/event/profile.h
|
64
64
|
- ext/io/event/selector/epoll.c
|
65
65
|
- ext/io/event/selector/epoll.h
|
66
66
|
- ext/io/event/selector/kqueue.c
|
67
67
|
- ext/io/event/selector/kqueue.h
|
68
|
-
- ext/io/event/selector/list.h
|
69
68
|
- ext/io/event/selector/pidfd.c
|
70
69
|
- ext/io/event/selector/selector.c
|
71
70
|
- ext/io/event/selector/selector.h
|
72
71
|
- ext/io/event/selector/uring.c
|
73
72
|
- ext/io/event/selector/uring.h
|
73
|
+
- ext/io/event/time.c
|
74
|
+
- ext/io/event/time.h
|
74
75
|
- lib/io/event.rb
|
75
76
|
- lib/io/event/debug/selector.rb
|
76
77
|
- lib/io/event/interrupt.rb
|
@@ -90,7 +91,6 @@ licenses:
|
|
90
91
|
metadata:
|
91
92
|
documentation_uri: https://socketry.github.io/io-event/
|
92
93
|
source_code_uri: https://github.com/socketry/io-event.git
|
93
|
-
post_install_message:
|
94
94
|
rdoc_options: []
|
95
95
|
require_paths:
|
96
96
|
- lib
|
@@ -105,8 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
105
|
- !ruby/object:Gem::Version
|
106
106
|
version: '0'
|
107
107
|
requirements: []
|
108
|
-
rubygems_version: 3.
|
109
|
-
signing_key:
|
108
|
+
rubygems_version: 3.6.2
|
110
109
|
specification_version: 4
|
111
110
|
summary: An event loop.
|
112
111
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|