polyphony 0.40 → 0.41
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
- data/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +6 -2
- data/Gemfile.lock +9 -6
- data/Rakefile +2 -2
- data/TODO.md +18 -97
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/nav.html +5 -5
- data/docs/api-reference/fiber.md +2 -2
- data/docs/main-concepts/design-principles.md +67 -9
- data/docs/main-concepts/extending.md +1 -1
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/{gyro → polyphony}/fiber.c +15 -19
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +503 -0
- data/ext/polyphony/libev_queue.c +214 -0
- data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -25
- data/ext/polyphony/polyphony.h +90 -0
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +14 -14
- data/ext/{gyro → polyphony}/thread.c +32 -115
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +16 -12
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +5 -5
- data/lib/polyphony/adapters/trace.rb +28 -28
- data/lib/polyphony/core/channel.rb +3 -3
- data/lib/polyphony/core/exceptions.rb +1 -1
- data/lib/polyphony/core/global_api.rb +11 -9
- data/lib/polyphony/core/resource_pool.rb +3 -3
- data/lib/polyphony/core/sync.rb +2 -2
- data/lib/polyphony/core/thread_pool.rb +6 -6
- data/lib/polyphony/core/throttler.rb +13 -6
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +20 -11
- data/lib/polyphony/extensions/fiber.rb +4 -4
- data/lib/polyphony/extensions/io.rb +56 -26
- data/lib/polyphony/extensions/openssl.rb +4 -8
- data/lib/polyphony/extensions/socket.rb +27 -9
- data/lib/polyphony/extensions/thread.rb +16 -9
- data/lib/polyphony/net.rb +9 -9
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +2 -2
- data/test/helper.rb +12 -1
- data/test/test_agent.rb +77 -0
- data/test/{test_async.rb → test_event.rb} +13 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +4 -4
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +33 -0
- data/test/test_thread.rb +37 -16
- data/test/test_trace.rb +6 -5
- metadata +24 -24
- data/ext/gyro/async.c +0 -132
- data/ext/gyro/child.c +0 -108
- data/ext/gyro/gyro.h +0 -158
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -457
- data/ext/gyro/queue.c +0 -146
- data/ext/gyro/selector.c +0 -205
- data/ext/gyro/signal.c +0 -99
- data/ext/gyro/timer.c +0 -115
- data/test/test_timer.rb +0 -56
@@ -1,75 +1,26 @@
|
|
1
|
-
#include "
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
static ID ID_push;
|
17
|
-
static ID ID_run_queue;
|
18
|
-
static ID ID_runnable_next;
|
19
|
-
static ID ID_stop;
|
20
|
-
|
21
|
-
VALUE event_selector_factory_proc(RB_BLOCK_CALL_FUNC_ARGLIST(args, klass)) {
|
22
|
-
return rb_funcall(klass, ID_new, 1, rb_ary_entry(args, 0));
|
23
|
-
}
|
24
|
-
|
25
|
-
static VALUE Thread_event_selector_set_proc(VALUE self, VALUE proc) {
|
26
|
-
if (!rb_obj_is_proc(proc)) {
|
27
|
-
proc = rb_proc_new(event_selector_factory_proc, proc);
|
28
|
-
}
|
29
|
-
rb_ivar_set(self, ID_ivar_event_selector_proc, proc);
|
30
|
-
return self;
|
31
|
-
}
|
32
|
-
|
33
|
-
static VALUE Thread_create_event_selector(VALUE self, VALUE thread) {
|
34
|
-
VALUE selector_proc = rb_ivar_get(self, ID_ivar_event_selector_proc);
|
35
|
-
if (selector_proc == Qnil) {
|
36
|
-
rb_raise(rb_eRuntimeError, "No event_selector_proc defined");
|
37
|
-
}
|
38
|
-
|
39
|
-
return rb_funcall(selector_proc, ID_call, 1, thread);
|
40
|
-
}
|
1
|
+
#include "polyphony.h"
|
2
|
+
|
3
|
+
ID ID_deactivate_all_watchers_post_fork;
|
4
|
+
ID ID_empty;
|
5
|
+
ID ID_fiber_ref_count;
|
6
|
+
ID ID_ivar_agent;
|
7
|
+
ID ID_ivar_join_wait_queue;
|
8
|
+
ID ID_ivar_main_fiber;
|
9
|
+
ID ID_ivar_result;
|
10
|
+
ID ID_ivar_terminated;
|
11
|
+
ID ID_pop;
|
12
|
+
ID ID_push;
|
13
|
+
ID ID_run_queue;
|
14
|
+
ID ID_runnable_next;
|
15
|
+
ID ID_stop;
|
41
16
|
|
42
17
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
43
18
|
VALUE queue;
|
44
|
-
VALUE selector;
|
45
19
|
|
46
20
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
47
21
|
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
|
48
22
|
queue = rb_ary_new();
|
49
23
|
rb_ivar_set(self, ID_run_queue, queue);
|
50
|
-
selector = rb_funcall(rb_cThread, ID_create_event_selector, 1, self);
|
51
|
-
rb_ivar_set(self, ID_ivar_event_selector, selector);
|
52
|
-
|
53
|
-
return self;
|
54
|
-
}
|
55
|
-
|
56
|
-
static VALUE Thread_stop_event_selector(VALUE self) {
|
57
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
58
|
-
if (selector != Qnil) {
|
59
|
-
rb_funcall(selector, ID_stop, 0);
|
60
|
-
}
|
61
|
-
// Nullify the selector in order to prevent running the
|
62
|
-
// selector after the thread is done running.
|
63
|
-
rb_ivar_set(self, ID_ivar_event_selector, Qnil);
|
64
|
-
|
65
|
-
return self;
|
66
|
-
}
|
67
|
-
|
68
|
-
static VALUE Thread_deactivate_all_watchers_post_fork(VALUE self) {
|
69
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
70
|
-
if (selector != Qnil) {
|
71
|
-
rb_funcall(selector, ID_deactivate_all_watchers_post_fork, 0);
|
72
|
-
}
|
73
24
|
|
74
25
|
return self;
|
75
26
|
}
|
@@ -101,15 +52,15 @@ static VALUE SYM_scheduled_fibers;
|
|
101
52
|
static VALUE SYM_pending_watchers;
|
102
53
|
|
103
54
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
55
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
104
56
|
VALUE stats = rb_hash_new();
|
105
57
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
106
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
107
58
|
long pending_count;
|
108
59
|
|
109
60
|
long scheduled_count = RARRAY_LEN(queue);
|
110
61
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
111
62
|
|
112
|
-
pending_count =
|
63
|
+
pending_count = LibevAgent_pending_count(agent);
|
113
64
|
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
114
65
|
|
115
66
|
return stats;
|
@@ -137,10 +88,8 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
137
88
|
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
138
89
|
// happen, not knowing that it there's already a fiber ready to run in its
|
139
90
|
// run queue.
|
140
|
-
VALUE
|
141
|
-
|
142
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
143
|
-
}
|
91
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
92
|
+
LibevAgent_break(agent);
|
144
93
|
}
|
145
94
|
return self;
|
146
95
|
}
|
@@ -168,13 +117,11 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
168
117
|
if (rb_thread_current() != self) {
|
169
118
|
// if the fiber scheduling is done across threads, we need to make sure the
|
170
119
|
// target thread is woken up in case it is in the middle of running its
|
171
|
-
// event
|
120
|
+
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
172
121
|
// happen, not knowing that it there's already a fiber ready to run in its
|
173
122
|
// run queue.
|
174
|
-
VALUE
|
175
|
-
|
176
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
177
|
-
}
|
123
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
124
|
+
LibevAgent_break(agent);
|
178
125
|
}
|
179
126
|
return self;
|
180
127
|
}
|
@@ -182,9 +129,9 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
182
129
|
VALUE Thread_switch_fiber(VALUE self) {
|
183
130
|
VALUE current_fiber = rb_fiber_current();
|
184
131
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
185
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
186
132
|
VALUE next_fiber;
|
187
133
|
VALUE value;
|
134
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
188
135
|
int ref_count;
|
189
136
|
|
190
137
|
if (__tracing_enabled__) {
|
@@ -193,31 +140,23 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
193
140
|
}
|
194
141
|
}
|
195
142
|
|
196
|
-
|
197
143
|
while (1) {
|
198
|
-
next_fiber = rb_ary_shift(queue);
|
199
|
-
// if (break_flag != 0) {
|
200
|
-
// return Qnil;
|
201
|
-
// }
|
202
144
|
ref_count = Thread_fiber_ref_count(self);
|
145
|
+
next_fiber = rb_ary_shift(queue);
|
203
146
|
if (next_fiber != Qnil) {
|
204
147
|
if (ref_count > 0) {
|
205
148
|
// this mechanism prevents event starvation in case the run queue never
|
206
149
|
// empties
|
207
|
-
|
150
|
+
LibevAgent_poll(agent, Qtrue, current_fiber, queue);
|
208
151
|
}
|
209
152
|
break;
|
210
153
|
}
|
211
|
-
if (ref_count == 0)
|
212
|
-
break;
|
213
|
-
}
|
154
|
+
if (ref_count == 0) break;
|
214
155
|
|
215
|
-
|
156
|
+
LibevAgent_poll(agent, Qnil, current_fiber, queue);
|
216
157
|
}
|
217
158
|
|
218
|
-
if (next_fiber == Qnil)
|
219
|
-
return Qnil;
|
220
|
-
}
|
159
|
+
if (next_fiber == Qnil) return Qnil;
|
221
160
|
|
222
161
|
// run next fiber
|
223
162
|
value = rb_ivar_get(next_fiber, ID_runnable_value);
|
@@ -236,14 +175,7 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
|
236
175
|
return self;
|
237
176
|
}
|
238
177
|
|
239
|
-
VALUE
|
240
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
241
|
-
Gyro_Selector_post_fork(selector);
|
242
|
-
|
243
|
-
return self;
|
244
|
-
}
|
245
|
-
|
246
|
-
VALUE Gyro_switchpoint() {
|
178
|
+
VALUE Polyphony_switchpoint() {
|
247
179
|
VALUE ret;
|
248
180
|
VALUE thread = rb_thread_current();
|
249
181
|
Thread_ref(thread);
|
@@ -253,17 +185,13 @@ VALUE Gyro_switchpoint() {
|
|
253
185
|
return ret;
|
254
186
|
}
|
255
187
|
|
256
|
-
VALUE Thread_current_event_selector() {
|
257
|
-
return rb_ivar_get(rb_thread_current(), ID_ivar_event_selector);
|
258
|
-
}
|
259
|
-
|
260
188
|
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
261
|
-
VALUE
|
189
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
262
190
|
if (fiber != Qnil) {
|
263
191
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
264
192
|
}
|
265
193
|
|
266
|
-
if (
|
194
|
+
if (LibevAgent_break(agent) == Qnil) {
|
267
195
|
// we're not inside the ev_loop, so we just do a switchpoint
|
268
196
|
Thread_switch_fiber(self);
|
269
197
|
}
|
@@ -272,19 +200,10 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
|
|
272
200
|
}
|
273
201
|
|
274
202
|
void Init_Thread() {
|
275
|
-
cQueue = rb_const_get(rb_cObject, rb_intern("Queue"));
|
276
|
-
|
277
|
-
rb_define_singleton_method(rb_cThread, "event_selector=", Thread_event_selector_set_proc, 1);
|
278
|
-
rb_define_singleton_method(rb_cThread, "create_event_selector", Thread_create_event_selector, 1);
|
279
|
-
|
280
203
|
rb_define_method(rb_cThread, "fiber_ref", Thread_ref, 0);
|
281
204
|
rb_define_method(rb_cThread, "fiber_unref", Thread_unref, 0);
|
282
205
|
|
283
|
-
rb_define_method(rb_cThread, "post_fork", Thread_post_fork, 0);
|
284
|
-
|
285
206
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
286
|
-
rb_define_method(rb_cThread, "stop_event_selector", Thread_stop_event_selector, 0);
|
287
|
-
rb_define_method(rb_cThread, "deactivate_all_watchers_post_fork", Thread_deactivate_all_watchers_post_fork, 0);
|
288
207
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
289
208
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
290
209
|
rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
|
@@ -294,12 +213,10 @@ void Init_Thread() {
|
|
294
213
|
Thread_schedule_fiber_with_priority, 2);
|
295
214
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
296
215
|
|
297
|
-
ID_create_event_selector = rb_intern("create_event_selector");
|
298
216
|
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
299
217
|
ID_empty = rb_intern("empty?");
|
300
218
|
ID_fiber_ref_count = rb_intern("fiber_ref_count");
|
301
|
-
|
302
|
-
ID_ivar_event_selector_proc = rb_intern("@event_selector_proc");
|
219
|
+
ID_ivar_agent = rb_intern("@agent");
|
303
220
|
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
304
221
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
305
222
|
ID_ivar_result = rb_intern("@result");
|
data/lib/polyphony.rb
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fiber'
|
4
|
-
require_relative './
|
4
|
+
require_relative './polyphony_ext'
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
module Polyphony
|
7
|
+
# Map Queue to Libev queue implementation
|
8
|
+
Queue = LibevQueue
|
9
|
+
end
|
8
10
|
|
9
11
|
require_relative './polyphony/extensions/core'
|
10
12
|
require_relative './polyphony/extensions/thread'
|
11
13
|
require_relative './polyphony/extensions/fiber'
|
12
14
|
require_relative './polyphony/extensions/io'
|
13
15
|
|
16
|
+
Thread.current.setup_fiber_scheduling
|
17
|
+
Thread.current.agent = Polyphony::LibevAgent.new
|
18
|
+
|
14
19
|
require_relative './polyphony/core/global_api'
|
15
20
|
require_relative './polyphony/core/resource_pool'
|
16
21
|
require_relative './polyphony/net'
|
17
22
|
require_relative './polyphony/adapters/process'
|
23
|
+
require_relative './polyphony/event'
|
18
24
|
|
19
25
|
# Main Polyphony API
|
20
26
|
module Polyphony
|
21
27
|
class << self
|
22
28
|
def wait_for_signal(sig)
|
23
29
|
fiber = Fiber.current
|
24
|
-
|
30
|
+
Polyphony.ref
|
25
31
|
old_trap = trap(sig) do
|
26
|
-
|
32
|
+
Polyphony.unref
|
27
33
|
fiber.schedule(sig)
|
28
34
|
trap(sig, old_trap)
|
29
35
|
end
|
@@ -32,12 +38,10 @@ module Polyphony
|
|
32
38
|
|
33
39
|
def fork(&block)
|
34
40
|
Kernel.fork do
|
35
|
-
|
36
|
-
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# created in the context of the forked process, which rescues *all*
|
40
|
-
# exceptions, including Interrupt and SystemExit.
|
41
|
+
# # Since the fiber doing the fork will become the main fiber of the
|
42
|
+
# # forked process, we leave it behind by transferring to a new fiber
|
43
|
+
# # created in the context of the forked process, which rescues *all*
|
44
|
+
# # exceptions, including Interrupt and SystemExit.
|
41
45
|
spin_forked_block(&block).transfer
|
42
46
|
end
|
43
47
|
end
|
@@ -63,9 +67,9 @@ module Polyphony
|
|
63
67
|
trap('SIGTERM', 'DEFAULT')
|
64
68
|
trap('SIGINT', 'DEFAULT')
|
65
69
|
|
66
|
-
Thread.current.post_fork
|
67
70
|
Thread.current.setup
|
68
71
|
Fiber.current.setup_main_fiber
|
72
|
+
Thread.current.agent.post_fork
|
69
73
|
|
70
74
|
install_terminating_signal_handlers
|
71
75
|
|
@@ -10,12 +10,13 @@ module ::PG
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.connect_async(conn)
|
13
|
+
socket_io = conn.socket_io
|
13
14
|
loop do
|
14
15
|
res = conn.connect_poll
|
15
16
|
case res
|
16
17
|
when PGRES_POLLING_FAILED then raise Error, conn.error_message
|
17
|
-
when PGRES_POLLING_READING then
|
18
|
-
when PGRES_POLLING_WRITING then
|
18
|
+
when PGRES_POLLING_READING then Thread.current.agent.wait_io(socket_io, false)
|
19
|
+
when PGRES_POLLING_WRITING then Thread.current.agent.wait_io(socket_io, true)
|
19
20
|
when PGRES_POLLING_OK then return conn.setnonblocking(true)
|
20
21
|
end
|
21
22
|
end
|
@@ -41,7 +42,7 @@ class ::PG::Connection
|
|
41
42
|
|
42
43
|
def get_result(&block)
|
43
44
|
while is_busy
|
44
|
-
|
45
|
+
Thread.current.agent.wait_io(socket_io, false)
|
45
46
|
consume_input
|
46
47
|
end
|
47
48
|
orig_get_result(&block)
|
@@ -58,7 +59,7 @@ class ::PG::Connection
|
|
58
59
|
|
59
60
|
def block(_timeout = 0)
|
60
61
|
while is_busy
|
61
|
-
|
62
|
+
Thread.current.agent.wait_io(socket_io, false)
|
62
63
|
consume_input
|
63
64
|
end
|
64
65
|
end
|
@@ -96,7 +97,7 @@ class ::PG::Connection
|
|
96
97
|
return move_on_after(timeout) { wait_for_notify(&block) } if timeout
|
97
98
|
|
98
99
|
loop do
|
99
|
-
|
100
|
+
Thread.current.agent.wait_io(socket_io, false)
|
100
101
|
consume_input
|
101
102
|
notice = notifies
|
102
103
|
next unless notice
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Polyphony
|
4
|
+
# Process patches
|
4
5
|
module Process
|
5
6
|
class << self
|
6
7
|
def watch(cmd = nil, &block)
|
7
8
|
terminated = nil
|
8
9
|
pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
|
9
|
-
|
10
|
-
watcher.await
|
10
|
+
Thread.current.agent.waitpid(pid)
|
11
11
|
terminated = true
|
12
12
|
ensure
|
13
13
|
kill_process(pid) unless terminated || pid.nil?
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def kill_process(pid)
|
17
17
|
cancel_after(5) do
|
18
18
|
kill_and_await('TERM', pid)
|
@@ -20,10 +20,10 @@ module Polyphony
|
|
20
20
|
rescue Polyphony::Cancel
|
21
21
|
kill_and_await(-9, pid)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def kill_and_await(sig, pid)
|
25
25
|
::Process.kill(sig, pid)
|
26
|
-
|
26
|
+
Thread.current.agent.waitpid(pid)
|
27
27
|
rescue SystemCallError
|
28
28
|
# ignore
|
29
29
|
puts 'SystemCallError in kill_and_await'
|
@@ -5,6 +5,7 @@ require_relative '../../polyphony'
|
|
5
5
|
STOCK_EVENTS = %i[line call return c_call c_return b_call b_return].freeze
|
6
6
|
|
7
7
|
module Polyphony
|
8
|
+
# Tracing functionality for Polyphony
|
8
9
|
module Trace
|
9
10
|
class << self
|
10
11
|
def new(*events)
|
@@ -12,10 +13,10 @@ module Polyphony
|
|
12
13
|
events = STOCK_EVENTS if events.empty?
|
13
14
|
::TracePoint.new(*events) { |tp| yield trace_record(tp, start_stamp) }
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
def trace_record(trp, start_stamp)
|
17
18
|
stamp = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start_stamp
|
18
|
-
|
19
|
+
|
19
20
|
{ stamp: stamp, event: trp.event, location: "#{trp.path}:#{trp.lineno}",
|
20
21
|
self: trp.self, binding: trp.binding, fiber: tp_fiber(trp),
|
21
22
|
lineno: trp.lineno, method_id: trp.method_id,
|
@@ -23,103 +24,103 @@ module Polyphony
|
|
23
24
|
return_value: tp_return_value(trp), schedule_value: tp_schedule_value(trp),
|
24
25
|
exception: tp_raised_exception(trp) }
|
25
26
|
end
|
26
|
-
|
27
|
+
|
27
28
|
def tp_fiber(trp)
|
28
29
|
trp.is_a?(FiberTracePoint) ? trp.fiber : Fiber.current
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
PARAMS_EVENTS = %i[call c_call b_call].freeze
|
32
|
-
|
33
|
+
|
33
34
|
def tp_params(trp)
|
34
35
|
PARAMS_EVENTS.include?(trp.event) ? trp.parameters : nil
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
RETURN_VALUE_EVENTS = %i[return c_return b_return].freeze
|
38
|
-
|
39
|
+
|
39
40
|
def tp_return_value(trp)
|
40
41
|
RETURN_VALUE_EVENTS.include?(trp.event) ? trp.return_value : nil
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
SCHEDULE_VALUE_EVENTS = %i[fiber_schedule fiber_run].freeze
|
44
|
-
|
45
|
+
|
45
46
|
def tp_schedule_value(trp)
|
46
47
|
SCHEDULE_VALUE_EVENTS.include?(trp.event) ? trp.value : nil
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
def tp_raised_exception(trp)
|
50
51
|
trp.event == :raise && trp.raised_exception
|
51
52
|
end
|
52
|
-
|
53
|
+
|
53
54
|
def analyze(records)
|
54
55
|
by_fiber = Hash.new { |h, f| h[f] = [] }
|
55
56
|
records.each_with_object(by_fiber) { |r, h| h[r[:fiber]] << r }
|
56
57
|
{ by_fiber: by_fiber }
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
# Implements fake TracePoint instances for fiber-related events
|
60
61
|
class FiberTracePoint
|
61
62
|
attr_reader :event, :fiber, :value
|
62
|
-
|
63
|
+
|
63
64
|
def initialize(tpoint)
|
64
65
|
@tp = tpoint
|
65
66
|
@event = tpoint.return_value[0]
|
66
67
|
@fiber = tpoint.return_value[1]
|
67
68
|
@value = tpoint.return_value[2]
|
68
69
|
end
|
69
|
-
|
70
|
+
|
70
71
|
def lineno
|
71
72
|
@tp.lineno
|
72
73
|
end
|
73
|
-
|
74
|
+
|
74
75
|
def method_id
|
75
76
|
@tp.method_id
|
76
77
|
end
|
77
|
-
|
78
|
+
|
78
79
|
def path
|
79
80
|
@tp.path
|
80
81
|
end
|
81
|
-
|
82
|
+
|
82
83
|
def self
|
83
84
|
@tp.self
|
84
85
|
end
|
85
|
-
|
86
|
+
|
86
87
|
def binding
|
87
88
|
@tp.binding
|
88
89
|
end
|
89
90
|
end
|
90
|
-
|
91
|
+
|
91
92
|
class << ::TracePoint
|
92
93
|
POLYPHONY_FILE_REGEXP = /^#{::Exception::POLYPHONY_DIR}/.freeze
|
93
|
-
|
94
|
+
|
94
95
|
alias_method :orig_new, :new
|
95
96
|
def new(*args, &block)
|
96
97
|
events_mask, fiber_events_mask = event_masks(args)
|
97
|
-
|
98
|
+
|
98
99
|
orig_new(*events_mask) do |tp|
|
99
100
|
handle_tp_event(tp, fiber_events_mask, &block)
|
100
101
|
end
|
101
102
|
end
|
102
|
-
|
103
|
+
|
103
104
|
def handle_tp_event(tpoint, fiber_events_mask)
|
104
105
|
# next unless !$watched_fiber || Fiber.current == $watched_fiber
|
105
|
-
|
106
|
+
|
106
107
|
if tpoint.method_id == :__fiber_trace__
|
107
108
|
return if tpoint.event != :c_return
|
108
109
|
return unless fiber_events_mask.include?(tpoint.return_value[0])
|
109
|
-
|
110
|
+
|
110
111
|
tpoint = FiberTracePoint.new(tpoint)
|
111
112
|
elsif tpoint.path =~ POLYPHONY_FILE_REGEXP
|
112
113
|
return
|
113
114
|
end
|
114
|
-
|
115
|
+
|
115
116
|
yield tpoint
|
116
117
|
end
|
117
|
-
|
118
|
+
|
118
119
|
ALL_FIBER_EVENTS = %i[
|
119
120
|
fiber_create fiber_terminate fiber_schedule fiber_switchpoint fiber_run
|
120
121
|
fiber_ev_loop_enter fiber_ev_loop_leave
|
121
122
|
].freeze
|
122
|
-
|
123
|
+
|
123
124
|
def event_masks(events)
|
124
125
|
events.each_with_object([[], []]) do |e, masks|
|
125
126
|
case e
|
@@ -135,4 +136,3 @@ module Polyphony
|
|
135
136
|
end
|
136
137
|
end
|
137
138
|
end
|
138
|
-
|