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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b0f569ddbe799cd62297cbe404f9799a5354cdb8a29bf0e75c2ae566bedc1fa
4
- data.tar.gz: dc7c358d32584e3f83bf13e4457e556606f38a8084408273b01e9cb5763284e5
3
+ metadata.gz: e8463c41263a6bb87c793fd0f45567ebc9cf8608f7d1c5453a5d22a6a0e47ae9
4
+ data.tar.gz: 8c603e1572174af54c78fd6bf0eaf5fe699fd744d4c46c5e8383e3bcea6b60c2
5
5
  SHA512:
6
- metadata.gz: 358ccfbdcf25cd12731e6f98583db917207411db261d69c5ed2adf3c3c4f84fcb6e572d7029365bb82c501b00c68d9af3726cbb74d3983e44b189d7d362eb147
7
- data.tar.gz: 63a5a659138caebfcd0fa8ebc08235127d556dc4c16d0e73f70613b360e702bc73f458156c40f79afdb2f93378e40859fd10c7be0df3e4cc7a355d29b8501325
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
@@ -8,27 +8,73 @@
8
8
 
9
9
  #include <stdio.h>
10
10
 
11
- void IO_Event_Profile_Call_initialize(struct IO_Event_Profile_Call *event) {
12
- event->enter_time.tv_sec = 0;
13
- event->enter_time.tv_nsec = 0;
14
- event->exit_time.tv_sec = 0;
15
- event->exit_time.tv_nsec = 0;
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
- event->nesting = 0;
19
+ call->nesting = 0;
18
20
 
19
- event->event_flag = 0;
20
- event->id = 0;
21
+ call->event_flag = 0;
22
+ call->id = 0;
21
23
 
22
- event->path = NULL;
23
- event->line = 0;
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 IO_Event_Profile_Call_free(struct IO_Event_Profile_Call *event) {
27
- if (event->path) {
28
- free((void*)event->path);
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 = (struct IO_Event_Profile*)data;
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 *event = IO_Event_Array_push(&profile->events);
45
- IO_Event_Time_current(&event->enter_time);
90
+ struct IO_Event_Profile_Call *call = IO_Event_Array_push(&profile->calls);
91
+ IO_Event_Time_current(&call->enter_time);
46
92
 
47
- event->event_flag = event_flag;
93
+ call->event_flag = event_flag;
48
94
 
49
- event->parent = profile->current;
50
- profile->current = event;
95
+ call->parent = profile->current;
96
+ profile->current = call;
51
97
 
52
- event->nesting = profile->nesting;
98
+ call->nesting = profile->nesting;
53
99
  profile->nesting += 1;
54
100
 
55
101
  if (id) {
56
- event->id = id;
57
- event->klass = klass;
102
+ call->id = id;
103
+ call->klass = klass;
58
104
  } else {
59
- rb_frame_method_id_and_class(&event->id, &event->klass);
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
- event->path = strdup(path);
110
+ call->path = strdup(path);
65
111
  }
66
- event->line = rb_sourceline();
112
+ call->line = rb_sourceline();
67
113
  } else if (event_flag_return_p(event_flag)) {
68
- struct IO_Event_Profile_Call *event = profile->current;
114
+ struct IO_Event_Profile_Call *call = profile->current;
69
115
 
70
- // Bad event sequence?
71
- if (event == NULL) return;
116
+ // Bad call sequence?
117
+ if (call == NULL) return;
72
118
 
73
- IO_Event_Time_current(&event->exit_time);
119
+ IO_Event_Time_current(&call->exit_time);
74
120
 
75
- profile->current = event->parent;
121
+ profile->current = call->parent;
76
122
  profile->nesting -= 1;
77
123
  }
78
124
  }
79
125
 
80
- void IO_Event_Profile_initialize(struct IO_Event_Profile *profile, VALUE fiber) {
81
- profile->fiber = fiber;
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
- VALUE thread = rb_thread_current();
96
- 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);
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(struct IO_Event_Profile *profile) {
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
- VALUE thread = rb_thread_current();
103
- rb_thread_remove_event_hook_with_data(thread, profile_event_callback, (VALUE)profile);
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, struct IO_Event_Profile *profile) {
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->events.limit; i += 1) {
119
- struct IO_Event_Profile_Call *event = profile->events.base[i];
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(&event->enter_time, &event->exit_time, &duration);
168
+ IO_Event_Time_elapsed(&call->enter_time, &call->exit_time, &duration);
124
169
 
125
- // Skip events that are too short to be meaningful:
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 < event->nesting; i += 1) {
176
+ for (size_t i = 0; i < call->nesting; i += 1) {
132
177
  fputc('\t', stream);
133
178
  }
134
179
 
135
- const char *name = rb_id2name(event->id);
136
- 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));
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 events that were too short to be meaningful.\n", skipped);
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
+ }
@@ -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
- VALUE fiber;
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 events;
40
+ struct IO_Event_Array calls;
39
41
  };
40
42
 
41
- void IO_Event_Profile_initialize(struct IO_Event_Profile *profile, VALUE fiber);
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 IO_Event_Profile_start(struct IO_Event_Profile *profile);
44
- void IO_Event_Profile_stop(struct IO_Event_Profile *profile);
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 IO_Event_Profile_free(struct IO_Event_Profile *profile);
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(struct IO_Event_Profile *profile) {
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
- struct IO_Event_Profile profile;
41
- IO_Event_Profile_initialize(&profile, fiber);
42
- IO_Event_Profile_start(&profile);
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(&profile);
48
+ IO_Event_Profile_stop(profile);
48
49
 
49
- float duration = IO_Event_Profile_duration(&profile);
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
- IO_Event_Profile_print(stderr, &profile);
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.001;
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 {
@@ -7,6 +7,6 @@
7
7
  class IO
8
8
  # @namespace
9
9
  module Event
10
- VERSION = "1.8.1"
10
+ VERSION = "1.8.2"
11
11
  end
12
12
  end
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.8.1
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file