io-event 1.8.1 → 1.8.2

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