io-event 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/io/event/event.c +4 -1
- data/ext/io/event/profile.c +109 -57
- data/ext/io/event/profile.h +16 -8
- data/ext/io/event/selector/selector.c +17 -7
- data/lib/io/event/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8463c41263a6bb87c793fd0f45567ebc9cf8608f7d1c5453a5d22a6a0e47ae9
|
4
|
+
data.tar.gz: 8c603e1572174af54c78fd6bf0eaf5fe699fd744d4c46c5e8383e3bcea6b60c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92bcff52e0f13b360faf313cbeeb639ea7c76c70019cdb8af46b709c81d8e290dd716d6c729ade019ccb9d2a7917c09fb20518f52b01ad720be075b1a2b686e8
|
7
|
+
data.tar.gz: f32893a69e6d22c1c279dc11b0038c755be115f5894625afa61031accc4bc652e205c85e9eb296bb613aecb1f3b3d9b6cbe9d845e8c63b1f4a0c50e703f930c7
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/ext/io/event/event.c
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
// Copyright, 2021-2025, by Samuel Williams.
|
3
3
|
|
4
4
|
#include "event.h"
|
5
|
+
#include "profile.h"
|
5
6
|
#include "selector/selector.h"
|
6
7
|
|
7
8
|
void Init_IO_Event(void)
|
@@ -11,8 +12,10 @@ void Init_IO_Event(void)
|
|
11
12
|
#endif
|
12
13
|
|
13
14
|
VALUE IO_Event = rb_define_module_under(rb_cIO, "Event");
|
14
|
-
VALUE IO_Event_Selector = rb_define_module_under(IO_Event, "Selector");
|
15
15
|
|
16
|
+
Init_IO_Event_Profile(IO_Event);
|
17
|
+
|
18
|
+
VALUE IO_Event_Selector = rb_define_module_under(IO_Event, "Selector");
|
16
19
|
Init_IO_Event_Selector(IO_Event_Selector);
|
17
20
|
|
18
21
|
#ifdef IO_EVENT_SELECTOR_URING
|
data/ext/io/event/profile.c
CHANGED
@@ -8,27 +8,73 @@
|
|
8
8
|
|
9
9
|
#include <stdio.h>
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
VALUE IO_Event_Profile = Qnil;
|
12
|
+
|
13
|
+
void IO_Event_Profile_Call_initialize(struct IO_Event_Profile_Call *call) {
|
14
|
+
call->enter_time.tv_sec = 0;
|
15
|
+
call->enter_time.tv_nsec = 0;
|
16
|
+
call->exit_time.tv_sec = 0;
|
17
|
+
call->exit_time.tv_nsec = 0;
|
16
18
|
|
17
|
-
|
19
|
+
call->nesting = 0;
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
+
call->event_flag = 0;
|
22
|
+
call->id = 0;
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
+
call->path = NULL;
|
25
|
+
call->line = 0;
|
26
|
+
}
|
27
|
+
|
28
|
+
void IO_Event_Profile_Call_free(struct IO_Event_Profile_Call *call) {
|
29
|
+
if (call->path) {
|
30
|
+
free((void*)call->path);
|
31
|
+
}
|
24
32
|
}
|
25
33
|
|
26
|
-
void
|
27
|
-
|
28
|
-
|
34
|
+
static void IO_Event_Profile_mark(void *ptr) {
|
35
|
+
struct IO_Event_Profile *profile = (struct IO_Event_Profile*)ptr;
|
36
|
+
|
37
|
+
// If `klass` is stored as a VALUE in calls, we need to mark them here:
|
38
|
+
for (size_t i = 0; i < profile->calls.limit; i += 1) {
|
39
|
+
struct IO_Event_Profile_Call *call = profile->calls.base[i];
|
40
|
+
rb_gc_mark(call->klass);
|
29
41
|
}
|
30
42
|
}
|
31
43
|
|
44
|
+
static void IO_Event_Profile_free(void *ptr) {
|
45
|
+
struct IO_Event_Profile *profile = (struct IO_Event_Profile*)ptr;
|
46
|
+
|
47
|
+
IO_Event_Array_free(&profile->calls);
|
48
|
+
|
49
|
+
free(profile);
|
50
|
+
}
|
51
|
+
|
52
|
+
const rb_data_type_t IO_Event_Profile_Type = {
|
53
|
+
.wrap_struct_name = "IO_Event_Profile",
|
54
|
+
.function = {
|
55
|
+
.dmark = IO_Event_Profile_mark,
|
56
|
+
.dfree = IO_Event_Profile_free,
|
57
|
+
},
|
58
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
59
|
+
};
|
60
|
+
|
61
|
+
VALUE IO_Event_Profile_allocate(VALUE klass) {
|
62
|
+
struct IO_Event_Profile *profile = ALLOC(struct IO_Event_Profile);
|
63
|
+
|
64
|
+
profile->calls.element_initialize = (void (*)(void*))IO_Event_Profile_Call_initialize;
|
65
|
+
profile->calls.element_free = (void (*)(void*))IO_Event_Profile_Call_free;
|
66
|
+
|
67
|
+
IO_Event_Array_initialize(&profile->calls, 0, sizeof(struct IO_Event_Profile_Call));
|
68
|
+
|
69
|
+
return TypedData_Wrap_Struct(klass, &IO_Event_Profile_Type, profile);
|
70
|
+
}
|
71
|
+
|
72
|
+
struct IO_Event_Profile *IO_Event_Profile_get(VALUE self) {
|
73
|
+
struct IO_Event_Profile *profile;
|
74
|
+
TypedData_Get_Struct(self, struct IO_Event_Profile, &IO_Event_Profile_Type, profile);
|
75
|
+
return profile;
|
76
|
+
}
|
77
|
+
|
32
78
|
int event_flag_call_p(rb_event_flag_t event_flags) {
|
33
79
|
return event_flags & (RUBY_EVENT_CALL | RUBY_EVENT_C_CALL);
|
34
80
|
}
|
@@ -38,105 +84,111 @@ int event_flag_return_p(rb_event_flag_t event_flags) {
|
|
38
84
|
}
|
39
85
|
|
40
86
|
static void profile_event_callback(rb_event_flag_t event_flag, VALUE data, VALUE self, ID id, VALUE klass) {
|
41
|
-
struct IO_Event_Profile *profile = (
|
87
|
+
struct IO_Event_Profile *profile = IO_Event_Profile_get(data);
|
42
88
|
|
43
89
|
if (event_flag_call_p(event_flag)) {
|
44
|
-
struct IO_Event_Profile_Call *
|
45
|
-
IO_Event_Time_current(&
|
90
|
+
struct IO_Event_Profile_Call *call = IO_Event_Array_push(&profile->calls);
|
91
|
+
IO_Event_Time_current(&call->enter_time);
|
46
92
|
|
47
|
-
|
93
|
+
call->event_flag = event_flag;
|
48
94
|
|
49
|
-
|
50
|
-
profile->current =
|
95
|
+
call->parent = profile->current;
|
96
|
+
profile->current = call;
|
51
97
|
|
52
|
-
|
98
|
+
call->nesting = profile->nesting;
|
53
99
|
profile->nesting += 1;
|
54
100
|
|
55
101
|
if (id) {
|
56
|
-
|
57
|
-
|
102
|
+
call->id = id;
|
103
|
+
call->klass = klass;
|
58
104
|
} else {
|
59
|
-
rb_frame_method_id_and_class(&
|
105
|
+
rb_frame_method_id_and_class(&call->id, &call->klass);
|
60
106
|
}
|
61
107
|
|
62
108
|
const char *path = rb_sourcefile();
|
63
109
|
if (path) {
|
64
|
-
|
110
|
+
call->path = strdup(path);
|
65
111
|
}
|
66
|
-
|
112
|
+
call->line = rb_sourceline();
|
67
113
|
} else if (event_flag_return_p(event_flag)) {
|
68
|
-
struct IO_Event_Profile_Call *
|
114
|
+
struct IO_Event_Profile_Call *call = profile->current;
|
69
115
|
|
70
|
-
// Bad
|
71
|
-
if (
|
116
|
+
// Bad call sequence?
|
117
|
+
if (call == NULL) return;
|
72
118
|
|
73
|
-
IO_Event_Time_current(&
|
119
|
+
IO_Event_Time_current(&call->exit_time);
|
74
120
|
|
75
|
-
profile->current =
|
121
|
+
profile->current = call->parent;
|
76
122
|
profile->nesting -= 1;
|
77
123
|
}
|
78
124
|
}
|
79
125
|
|
80
|
-
void
|
81
|
-
profile
|
126
|
+
void IO_Event_Profile_start(VALUE self, int track_calls) {
|
127
|
+
struct IO_Event_Profile *profile = IO_Event_Profile_get(self);
|
82
128
|
|
83
|
-
profile->events.element_initialize = (void (*)(void*))IO_Event_Profile_Call_initialize;
|
84
|
-
profile->events.element_free = (void (*)(void*))IO_Event_Profile_Call_free;
|
85
|
-
|
86
|
-
IO_Event_Array_initialize(&profile->events, 0, sizeof(struct IO_Event_Profile_Call));
|
87
|
-
}
|
88
|
-
|
89
|
-
void IO_Event_Profile_start(struct IO_Event_Profile *profile) {
|
90
129
|
IO_Event_Time_current(&profile->start_time);
|
91
130
|
profile->nesting = 0;
|
92
131
|
profile->current = NULL;
|
93
132
|
|
133
|
+
profile->track_calls = track_calls;
|
134
|
+
|
94
135
|
// Since fibers are currently limited to a single thread, we use this in the hope that it's a little more efficient:
|
95
|
-
|
96
|
-
|
136
|
+
if (profile->track_calls) {
|
137
|
+
VALUE thread = rb_thread_current();
|
138
|
+
rb_thread_add_event_hook(thread, profile_event_callback, RUBY_EVENT_CALL | RUBY_EVENT_C_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN, self);
|
139
|
+
}
|
97
140
|
}
|
98
141
|
|
99
|
-
void IO_Event_Profile_stop(
|
142
|
+
void IO_Event_Profile_stop(VALUE self) {
|
143
|
+
struct IO_Event_Profile *profile = IO_Event_Profile_get(self);
|
144
|
+
|
100
145
|
IO_Event_Time_current(&profile->stop_time);
|
101
146
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
void IO_Event_Profile_free(struct IO_Event_Profile *profile) {
|
107
|
-
IO_Event_Array_free(&profile->events);
|
147
|
+
if (profile->track_calls) {
|
148
|
+
VALUE thread = rb_thread_current();
|
149
|
+
rb_thread_remove_event_hook_with_data(thread, profile_event_callback, self);
|
150
|
+
}
|
108
151
|
}
|
109
152
|
|
110
153
|
static const float IO_EVENT_PROFILE_PRINT_MINIMUM_PROPORTION = 0.01;
|
111
154
|
|
112
|
-
void IO_Event_Profile_print(FILE *restrict stream
|
155
|
+
void IO_Event_Profile_print(VALUE self, FILE *restrict stream) {
|
156
|
+
struct IO_Event_Profile *profile = IO_Event_Profile_get(self);
|
157
|
+
|
113
158
|
struct timespec total_duration = {};
|
114
159
|
IO_Event_Time_elapsed(&profile->start_time, &profile->stop_time, &total_duration);
|
115
160
|
|
116
161
|
size_t skipped = 0;
|
117
162
|
|
118
|
-
for (size_t i = 0; i < profile->
|
119
|
-
struct IO_Event_Profile_Call *
|
163
|
+
for (size_t i = 0; i < profile->calls.limit; i += 1) {
|
164
|
+
struct IO_Event_Profile_Call *call = profile->calls.base[i];
|
120
165
|
|
121
166
|
struct timespec duration = {};
|
122
167
|
|
123
|
-
IO_Event_Time_elapsed(&
|
168
|
+
IO_Event_Time_elapsed(&call->enter_time, &call->exit_time, &duration);
|
124
169
|
|
125
|
-
// Skip
|
170
|
+
// Skip calls that are too short to be meaningful:
|
126
171
|
if (IO_Event_Time_proportion(&duration, &total_duration) < IO_EVENT_PROFILE_PRINT_MINIMUM_PROPORTION) {
|
127
172
|
skipped += 1;
|
128
173
|
continue;
|
129
174
|
}
|
130
175
|
|
131
|
-
for (size_t i = 0; i <
|
176
|
+
for (size_t i = 0; i < call->nesting; i += 1) {
|
132
177
|
fputc('\t', stream);
|
133
178
|
}
|
134
179
|
|
135
|
-
|
136
|
-
|
180
|
+
VALUE class_inspect = rb_inspect(call->klass);
|
181
|
+
const char *name = rb_id2name(call->id);
|
182
|
+
|
183
|
+
fprintf(stream, "\t%s:%d in '%s#%s' (" IO_EVENT_TIME_PRINTF_TIMESPEC "s)\n", call->path, call->line, RSTRING_PTR(class_inspect), name, IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(duration));
|
137
184
|
}
|
138
185
|
|
139
186
|
if (skipped > 0) {
|
140
|
-
fprintf(stream, "Skipped %zu
|
187
|
+
fprintf(stream, "Skipped %zu calls that were too short to be meaningful.\n", skipped);
|
141
188
|
}
|
142
189
|
}
|
190
|
+
|
191
|
+
void Init_IO_Event_Profile(VALUE IO_Event) {
|
192
|
+
IO_Event_Profile = rb_define_class_under(IO_Event, "Profile", rb_cObject);
|
193
|
+
rb_define_alloc_func(IO_Event_Profile, IO_Event_Profile_allocate);
|
194
|
+
}
|
data/ext/io/event/profile.h
CHANGED
@@ -7,6 +7,8 @@
|
|
7
7
|
#include "array.h"
|
8
8
|
#include "time.h"
|
9
9
|
|
10
|
+
extern VALUE IO_Event_Profile;
|
11
|
+
|
10
12
|
struct IO_Event_Profile_Call {
|
11
13
|
struct timespec enter_time;
|
12
14
|
struct timespec exit_time;
|
@@ -24,7 +26,7 @@ struct IO_Event_Profile_Call {
|
|
24
26
|
};
|
25
27
|
|
26
28
|
struct IO_Event_Profile {
|
27
|
-
|
29
|
+
int track_calls;
|
28
30
|
|
29
31
|
struct timespec start_time;
|
30
32
|
struct timespec stop_time;
|
@@ -35,21 +37,27 @@ struct IO_Event_Profile {
|
|
35
37
|
// The current call frame:
|
36
38
|
struct IO_Event_Profile_Call *current;
|
37
39
|
|
38
|
-
struct IO_Event_Array
|
40
|
+
struct IO_Event_Array calls;
|
39
41
|
};
|
40
42
|
|
41
|
-
|
43
|
+
extern const rb_data_type_t IO_Event_Profile_Type;
|
44
|
+
VALUE IO_Event_Profile_allocate(VALUE klass);
|
45
|
+
struct IO_Event_Profile *IO_Event_Profile_get(VALUE self);
|
42
46
|
|
43
|
-
void
|
44
|
-
void
|
47
|
+
void IO_Event_Profile_initialize(struct IO_Event_Profile *profile, VALUE fiber);
|
48
|
+
void IO_Event_Profile_start(VALUE self, int track_calls);
|
49
|
+
void IO_Event_Profile_stop(VALUE self);
|
45
50
|
|
46
|
-
void
|
47
|
-
void IO_Event_Profile_print(FILE *restrict stream, struct IO_Event_Profile *profile);
|
51
|
+
void IO_Event_Profile_print(VALUE profile, FILE *restrict stream);
|
48
52
|
|
49
|
-
static inline float IO_Event_Profile_duration(
|
53
|
+
static inline float IO_Event_Profile_duration(VALUE self) {
|
54
|
+
struct IO_Event_Profile *profile = IO_Event_Profile_get(self);
|
55
|
+
|
50
56
|
struct timespec duration;
|
51
57
|
|
52
58
|
IO_Event_Time_elapsed(&profile->start_time, &profile->stop_time, &duration);
|
53
59
|
|
54
60
|
return IO_Event_Time_duration(&duration);
|
55
61
|
}
|
62
|
+
|
63
|
+
void Init_IO_Event_Profile(VALUE IO_Event);
|
@@ -12,6 +12,7 @@ static const int DEBUG = 0;
|
|
12
12
|
static ID id_transfer, id_alive_p;
|
13
13
|
|
14
14
|
static float IO_Event_Selector_stall_log_threshold = 0;
|
15
|
+
static int IO_Event_Selector_stall_log_profile = 0;
|
15
16
|
|
16
17
|
VALUE IO_Event_Selector_fiber_transfer(VALUE fiber, int argc, VALUE *argv) {
|
17
18
|
// TODO Consider introducing something like `rb_fiber_scheduler_transfer(...)`.
|
@@ -37,20 +38,21 @@ VALUE IO_Event_Selector_fiber_transfer_user(VALUE fiber, int argc, VALUE *argv)
|
|
37
38
|
return IO_Event_Selector_fiber_transfer(fiber, argc, argv);
|
38
39
|
}
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
IO_Event_Profile_start(
|
41
|
+
VALUE profile = IO_Event_Profile_allocate(IO_Event_Profile);
|
42
|
+
|
43
|
+
IO_Event_Profile_start(profile, IO_Event_Selector_stall_log_profile);
|
43
44
|
|
44
45
|
// Transfer control to the fiber:
|
45
46
|
VALUE result = IO_Event_Selector_fiber_transfer(fiber, argc, argv);
|
46
47
|
|
47
|
-
IO_Event_Profile_stop(
|
48
|
+
IO_Event_Profile_stop(profile);
|
48
49
|
|
49
|
-
float duration = IO_Event_Profile_duration(
|
50
|
+
float duration = IO_Event_Profile_duration(profile);
|
50
51
|
|
51
52
|
if (duration > IO_Event_Selector_stall_log_threshold) {
|
52
53
|
fprintf(stderr, "Fiber stalled for %.3f seconds\n", duration);
|
53
|
-
|
54
|
+
|
55
|
+
IO_Event_Profile_print(profile, stderr);
|
54
56
|
}
|
55
57
|
|
56
58
|
return result;
|
@@ -179,7 +181,7 @@ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
179
181
|
|
180
182
|
if (stall_log_threshold) {
|
181
183
|
if (strcmp(stall_log_threshold, "true") == 0) {
|
182
|
-
IO_Event_Selector_stall_log_threshold = 0.
|
184
|
+
IO_Event_Selector_stall_log_threshold = 0.01;
|
183
185
|
} else if (strcmp(stall_log_threshold, "false") == 0) {
|
184
186
|
IO_Event_Selector_stall_log_threshold = 0;
|
185
187
|
} else {
|
@@ -188,6 +190,14 @@ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
188
190
|
|
189
191
|
if (DEBUG) fprintf(stderr, "IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD = %.3f\n", IO_Event_Selector_stall_log_threshold);
|
190
192
|
}
|
193
|
+
|
194
|
+
char *stall_log_profile = getenv("IO_EVENT_SELECTOR_STALL_LOG_PROFILE");
|
195
|
+
|
196
|
+
if (stall_log_profile) {
|
197
|
+
if (strcmp(stall_log_profile, "true") == 0) {
|
198
|
+
IO_Event_Selector_stall_log_profile = 1;
|
199
|
+
}
|
200
|
+
}
|
191
201
|
}
|
192
202
|
|
193
203
|
struct wait_and_transfer_arguments {
|
data/lib/io/event/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
Binary file
|