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.
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
-