polyphony 0.40 → 0.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +6 -2
  6. data/Gemfile.lock +9 -6
  7. data/Rakefile +2 -2
  8. data/TODO.md +18 -97
  9. data/docs/_includes/head.html +40 -0
  10. data/docs/_includes/nav.html +5 -5
  11. data/docs/api-reference/fiber.md +2 -2
  12. data/docs/main-concepts/design-principles.md +67 -9
  13. data/docs/main-concepts/extending.md +1 -1
  14. data/examples/core/xx-agent.rb +102 -0
  15. data/examples/core/xx-sleeping.rb +14 -6
  16. data/examples/io/xx-irb.rb +1 -1
  17. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  18. data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
  19. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  20. data/ext/{gyro → polyphony}/fiber.c +15 -19
  21. data/ext/{gyro → polyphony}/libev.c +0 -0
  22. data/ext/{gyro → polyphony}/libev.h +0 -0
  23. data/ext/polyphony/libev_agent.c +503 -0
  24. data/ext/polyphony/libev_queue.c +214 -0
  25. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -25
  26. data/ext/polyphony/polyphony.h +90 -0
  27. data/ext/polyphony/polyphony_ext.c +23 -0
  28. data/ext/{gyro → polyphony}/socket.c +14 -14
  29. data/ext/{gyro → polyphony}/thread.c +32 -115
  30. data/ext/{gyro → polyphony}/tracing.c +1 -1
  31. data/lib/polyphony.rb +16 -12
  32. data/lib/polyphony/adapters/irb.rb +1 -1
  33. data/lib/polyphony/adapters/postgres.rb +6 -5
  34. data/lib/polyphony/adapters/process.rb +5 -5
  35. data/lib/polyphony/adapters/trace.rb +28 -28
  36. data/lib/polyphony/core/channel.rb +3 -3
  37. data/lib/polyphony/core/exceptions.rb +1 -1
  38. data/lib/polyphony/core/global_api.rb +11 -9
  39. data/lib/polyphony/core/resource_pool.rb +3 -3
  40. data/lib/polyphony/core/sync.rb +2 -2
  41. data/lib/polyphony/core/thread_pool.rb +6 -6
  42. data/lib/polyphony/core/throttler.rb +13 -6
  43. data/lib/polyphony/event.rb +27 -0
  44. data/lib/polyphony/extensions/core.rb +20 -11
  45. data/lib/polyphony/extensions/fiber.rb +4 -4
  46. data/lib/polyphony/extensions/io.rb +56 -26
  47. data/lib/polyphony/extensions/openssl.rb +4 -8
  48. data/lib/polyphony/extensions/socket.rb +27 -9
  49. data/lib/polyphony/extensions/thread.rb +16 -9
  50. data/lib/polyphony/net.rb +9 -9
  51. data/lib/polyphony/version.rb +1 -1
  52. data/polyphony.gemspec +2 -2
  53. data/test/helper.rb +12 -1
  54. data/test/test_agent.rb +77 -0
  55. data/test/{test_async.rb → test_event.rb} +13 -7
  56. data/test/test_ext.rb +25 -4
  57. data/test/test_fiber.rb +19 -10
  58. data/test/test_global_api.rb +4 -4
  59. data/test/test_io.rb +46 -24
  60. data/test/test_queue.rb +74 -0
  61. data/test/test_signal.rb +3 -40
  62. data/test/test_socket.rb +33 -0
  63. data/test/test_thread.rb +37 -16
  64. data/test/test_trace.rb +6 -5
  65. metadata +24 -24
  66. data/ext/gyro/async.c +0 -132
  67. data/ext/gyro/child.c +0 -108
  68. data/ext/gyro/gyro.h +0 -158
  69. data/ext/gyro/gyro_ext.c +0 -33
  70. data/ext/gyro/io.c +0 -457
  71. data/ext/gyro/queue.c +0 -146
  72. data/ext/gyro/selector.c +0 -205
  73. data/ext/gyro/signal.c +0 -99
  74. data/ext/gyro/timer.c +0 -115
  75. data/test/test_timer.rb +0 -56
@@ -1,75 +1,26 @@
1
- #include "gyro.h"
2
-
3
- static VALUE cQueue;
4
-
5
- static ID ID_create_event_selector;
6
- static ID ID_deactivate_all_watchers_post_fork;
7
- static ID ID_empty;
8
- static ID ID_fiber_ref_count;
9
- static ID ID_ivar_event_selector_proc;
10
- static ID ID_ivar_event_selector;
11
- static ID ID_ivar_join_wait_queue;
12
- static ID ID_ivar_main_fiber;
13
- static ID ID_ivar_result;
14
- static ID ID_ivar_terminated;
15
- static ID ID_pop;
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 = Gyro_Selector_pending_count(selector);
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 selector = rb_ivar_get(self, ID_ivar_event_selector);
141
- if (selector != Qnil) {
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 selector. Otherwise it's gonna be stuck waiting for an event to
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 selector = rb_ivar_get(self, ID_ivar_event_selector);
175
- if (selector != Qnil) {
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
- Gyro_Selector_run_no_wait(selector, current_fiber, RARRAY_LEN(queue));
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
- Gyro_Selector_run(selector, current_fiber);
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 Thread_post_fork(VALUE self) {
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 selector = rb_ivar_get(self, ID_ivar_event_selector);
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 (Gyro_Selector_break_out_of_ev_loop(selector) == Qnil) {
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
- ID_ivar_event_selector = rb_intern("@event_selector");
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");
@@ -1,4 +1,4 @@
1
- #include "gyro.h"
1
+ #include "polyphony.h"
2
2
 
3
3
  int __tracing_enabled__ = 0;
4
4
 
@@ -1,29 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fiber'
4
- require_relative './gyro_ext'
4
+ require_relative './polyphony_ext'
5
5
 
6
- Thread.event_selector = Gyro::Selector
7
- Thread.current.setup_fiber_scheduling
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
- Gyro.ref
30
+ Polyphony.ref
25
31
  old_trap = trap(sig) do
26
- Gyro.unref
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
- Gyro.incr_generation
36
-
37
- # Since the fiber doing the fork will become the main fiber of the
38
- # forked process, we leave it behind by transferring to a new fiber
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
 
@@ -16,7 +16,7 @@ if Object.constants.include?(:Reline)
16
16
  fiber.cancel
17
17
  end
18
18
  read_ios.each do |io|
19
- io.read_watcher.await
19
+ Thread.current.agent.wait_io(io, false)
20
20
  return [io]
21
21
  end
22
22
  rescue Polyphony::Cancel
@@ -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 conn.socket_io.read_watcher.await
18
- when PGRES_POLLING_WRITING then conn.socket_io.write_watcher.await
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
- socket_io.read_watcher.await
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
- socket_io.read_watcher.await
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
- socket_io.read_watcher.await
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
- watcher = Gyro::Child.new(pid)
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
- Gyro::Child.new(pid).await
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
-