polyphony 0.77 → 0.80

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +18 -0
  4. data/Gemfile.lock +2 -1
  5. data/examples/core/pingpong.rb +7 -4
  6. data/examples/core/zlib_stream.rb +15 -0
  7. data/ext/polyphony/backend_common.c +16 -8
  8. data/ext/polyphony/backend_common.h +8 -3
  9. data/ext/polyphony/backend_io_uring.c +19 -3
  10. data/ext/polyphony/backend_libev.c +33 -17
  11. data/ext/polyphony/fiber.c +28 -28
  12. data/ext/polyphony/polyphony.c +1 -8
  13. data/ext/polyphony/polyphony.h +11 -8
  14. data/ext/polyphony/queue.c +82 -6
  15. data/ext/polyphony/thread.c +6 -2
  16. data/lib/polyphony/adapters/fs.rb +4 -0
  17. data/lib/polyphony/adapters/process.rb +14 -1
  18. data/lib/polyphony/adapters/redis.rb +28 -0
  19. data/lib/polyphony/adapters/sequel.rb +19 -1
  20. data/lib/polyphony/core/debug.rb +203 -0
  21. data/lib/polyphony/core/exceptions.rb +21 -6
  22. data/lib/polyphony/core/global_api.rb +228 -73
  23. data/lib/polyphony/core/resource_pool.rb +65 -20
  24. data/lib/polyphony/core/sync.rb +57 -12
  25. data/lib/polyphony/core/thread_pool.rb +42 -5
  26. data/lib/polyphony/core/throttler.rb +21 -5
  27. data/lib/polyphony/core/timer.rb +125 -1
  28. data/lib/polyphony/extensions/exception.rb +36 -6
  29. data/lib/polyphony/extensions/fiber.rb +244 -61
  30. data/lib/polyphony/extensions/io.rb +4 -2
  31. data/lib/polyphony/extensions/kernel.rb +9 -4
  32. data/lib/polyphony/extensions/object.rb +8 -0
  33. data/lib/polyphony/extensions/openssl.rb +3 -1
  34. data/lib/polyphony/extensions/socket.rb +458 -39
  35. data/lib/polyphony/extensions/thread.rb +108 -43
  36. data/lib/polyphony/extensions/timeout.rb +12 -1
  37. data/lib/polyphony/extensions.rb +1 -0
  38. data/lib/polyphony/net.rb +59 -0
  39. data/lib/polyphony/version.rb +1 -1
  40. data/lib/polyphony.rb +0 -2
  41. data/test/test_backend.rb +6 -2
  42. data/test/test_global_api.rb +0 -23
  43. data/test/test_io.rb +7 -7
  44. data/test/test_queue.rb +103 -1
  45. data/test/test_resource_pool.rb +1 -1
  46. data/test/test_signal.rb +15 -15
  47. data/test/test_supervise.rb +27 -0
  48. data/test/test_thread.rb +1 -1
  49. data/test/test_throttler.rb +0 -6
  50. data/test/test_trace.rb +189 -24
  51. metadata +9 -8
  52. data/lib/polyphony/core/channel.rb +0 -15
@@ -2,6 +2,7 @@
2
2
  #include "ring_buffer.h"
3
3
 
4
4
  typedef struct queue {
5
+ unsigned int closed;
5
6
  ring_buffer values;
6
7
  ring_buffer shift_queue;
7
8
  ring_buffer push_queue;
@@ -9,6 +10,8 @@ typedef struct queue {
9
10
  } Queue_t;
10
11
 
11
12
  VALUE cQueue = Qnil;
13
+ VALUE cClosedQueueError = Qnil;
14
+ VALUE cThreadError = Qnil;
12
15
 
13
16
  static void Queue_mark(void *ptr) {
14
17
  Queue_t *queue = ptr;
@@ -49,6 +52,7 @@ static VALUE Queue_initialize(int argc, VALUE *argv, VALUE self) {
49
52
  Queue_t *queue;
50
53
  GetQueue(self, queue);
51
54
 
55
+ queue->closed = 0;
52
56
  ring_buffer_init(&queue->values);
53
57
  ring_buffer_init(&queue->shift_queue);
54
58
  ring_buffer_init(&queue->push_queue);
@@ -99,6 +103,9 @@ VALUE Queue_push(VALUE self, VALUE value) {
99
103
  Queue_t *queue;
100
104
  GetQueue(self, queue);
101
105
 
106
+ if (queue->closed)
107
+ rb_raise(cClosedQueueError, "queue closed");
108
+
102
109
  if (queue->capacity) capped_queue_block_push(queue);
103
110
 
104
111
  queue_schedule_first_blocked_fiber(&queue->shift_queue);
@@ -111,6 +118,9 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
111
118
  Queue_t *queue;
112
119
  GetQueue(self, queue);
113
120
 
121
+ if (queue->closed)
122
+ rb_raise(cClosedQueueError, "queue closed");
123
+
114
124
  if (queue->capacity) capped_queue_block_push(queue);
115
125
 
116
126
  queue_schedule_first_blocked_fiber(&queue->shift_queue);
@@ -119,14 +129,25 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
119
129
  return self;
120
130
  }
121
131
 
122
- VALUE Queue_shift(VALUE self) {
123
- Queue_t *queue;
132
+ VALUE Queue_shift_nonblock(Queue_t *queue) {
133
+ if (queue->values.count) {
134
+ VALUE value = ring_buffer_shift(&queue->values);
135
+ if ((queue->capacity) && (queue->capacity > queue->values.count))
136
+ queue_schedule_first_blocked_fiber(&queue->push_queue);
137
+ RB_GC_GUARD(value);
138
+ return value;
139
+ }
140
+ rb_raise(cThreadError, "queue empty");
141
+ }
142
+
143
+ VALUE Queue_shift_block(Queue_t *queue) {
124
144
  VALUE fiber = rb_fiber_current();
125
145
  VALUE thread = rb_thread_current();
126
146
  VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
127
147
  VALUE value;
128
148
 
129
- GetQueue(self, queue);
149
+ if (queue->closed && !queue->values.count)
150
+ rb_raise(cClosedQueueError, "queue closed");
130
151
 
131
152
  while (1) {
132
153
  VALUE switchpoint_result;
@@ -140,6 +161,7 @@ VALUE Queue_shift(VALUE self) {
140
161
  RAISE_IF_EXCEPTION(switchpoint_result);
141
162
  RB_GC_GUARD(switchpoint_result);
142
163
  if (queue->values.count) break;
164
+ if (queue->closed) return Qnil;
143
165
  }
144
166
  value = ring_buffer_shift(&queue->values);
145
167
  if ((queue->capacity) && (queue->capacity > queue->values.count))
@@ -148,6 +170,16 @@ VALUE Queue_shift(VALUE self) {
148
170
  return value;
149
171
  }
150
172
 
173
+ VALUE Queue_shift(int argc,VALUE *argv, VALUE self) {
174
+ int nonblock = argc && RTEST(argv[0]);
175
+ Queue_t *queue;
176
+ GetQueue(self, queue);
177
+
178
+ return nonblock ?
179
+ Queue_shift_nonblock(queue) :
180
+ Queue_shift_block(queue);
181
+ }
182
+
151
183
  VALUE Queue_delete(VALUE self, VALUE value) {
152
184
  Queue_t *queue;
153
185
  GetQueue(self, queue);
@@ -244,6 +276,13 @@ VALUE Queue_pending_p(VALUE self) {
244
276
  return (queue->shift_queue.count) ? Qtrue : Qfalse;
245
277
  }
246
278
 
279
+ VALUE Queue_num_waiting(VALUE self) {
280
+ Queue_t *queue;
281
+ GetQueue(self, queue);
282
+
283
+ return INT2NUM(queue->shift_queue.count);
284
+ }
285
+
247
286
  VALUE Queue_size_m(VALUE self) {
248
287
  Queue_t *queue;
249
288
  GetQueue(self, queue);
@@ -251,20 +290,54 @@ VALUE Queue_size_m(VALUE self) {
251
290
  return INT2NUM(queue->values.count);
252
291
  }
253
292
 
293
+ VALUE Queue_closed_p(VALUE self) {
294
+ Queue_t *queue;
295
+ GetQueue(self, queue);
296
+
297
+ return (queue->closed) ? Qtrue : Qfalse;
298
+ }
299
+
300
+ VALUE Queue_close(VALUE self) {
301
+ Queue_t *queue;
302
+ GetQueue(self, queue);
303
+
304
+ if (queue->closed) goto end;
305
+ queue->closed = 1;
306
+
307
+ // release all fibers waiting on `#shift`
308
+ while (queue->shift_queue.count) {
309
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
310
+ if (fiber == Qnil) break;
311
+ Fiber_make_runnable(fiber, Qnil);
312
+ }
313
+
314
+ end:
315
+ return self;
316
+ }
317
+
254
318
  void Init_Queue() {
319
+ cClosedQueueError = rb_const_get(rb_cObject, rb_intern("ClosedQueueError"));
320
+ cThreadError = rb_const_get(rb_cObject, rb_intern("ThreadError"));
321
+
255
322
  cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cObject);
256
323
  rb_define_alloc_func(cQueue, Queue_allocate);
257
324
 
258
325
  rb_define_method(cQueue, "initialize", Queue_initialize, -1);
259
326
  rb_define_method(cQueue, "push", Queue_push, 1);
260
327
  rb_define_method(cQueue, "<<", Queue_push, 1);
328
+ rb_define_method(cQueue, "enq", Queue_push, 1);
261
329
  rb_define_method(cQueue, "unshift", Queue_unshift, 1);
262
330
 
263
- rb_define_method(cQueue, "shift", Queue_shift, 0);
264
- rb_define_method(cQueue, "pop", Queue_shift, 0);
331
+ rb_define_method(cQueue, "shift", Queue_shift, -1);
332
+ rb_define_method(cQueue, "pop", Queue_shift, -1);
333
+ rb_define_method(cQueue, "deq", Queue_shift, -1);
334
+
265
335
  rb_define_method(cQueue, "delete", Queue_delete, 1);
266
336
  rb_define_method(cQueue, "clear", Queue_clear, 0);
267
337
 
338
+ rb_define_method(cQueue, "size", Queue_size_m, 0);
339
+ rb_define_method(cQueue, "length", Queue_size_m, 0);
340
+
268
341
  rb_define_method(cQueue, "cap", Queue_cap, 1);
269
342
  rb_define_method(cQueue, "capped?", Queue_capped_p, 0);
270
343
 
@@ -273,5 +346,8 @@ void Init_Queue() {
273
346
  rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
274
347
  rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
275
348
  rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
276
- rb_define_method(cQueue, "size", Queue_size_m, 0);
349
+ rb_define_method(cQueue, "num_waiting", Queue_num_waiting, 0);
350
+
351
+ rb_define_method(cQueue, "closed?", Queue_closed_p, 0);
352
+ rb_define_method(cQueue, "close", Queue_close, 0);
277
353
  }
@@ -23,11 +23,15 @@ VALUE Thread_fiber_unschedule(VALUE self, VALUE fiber) {
23
23
  }
24
24
 
25
25
  inline void Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
26
- schedule_fiber(self, fiber, value, 0);
26
+ Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, 0);
27
+
28
+ // schedule_fiber(self, fiber, value, 0);
27
29
  }
28
30
 
29
31
  inline void Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
30
- schedule_fiber(self, fiber, value, 1);
32
+ Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, 1);
33
+
34
+ // schedule_fiber(self, fiber, value, 1);
31
35
  }
32
36
 
33
37
  VALUE Thread_switch_fiber(VALUE self) {
@@ -6,6 +6,8 @@ require_relative '../core/thread_pool'
6
6
 
7
7
  ::File.singleton_class.instance_eval do
8
8
  alias_method :orig_stat, :stat
9
+
10
+ # Offloads `File.stat` to the default thread pool.
9
11
  def stat(path)
10
12
  ThreadPool.process { orig_stat(path) }
11
13
  end
@@ -13,6 +15,8 @@ end
13
15
 
14
16
  ::IO.singleton_class.instance_eval do
15
17
  alias_method :orig_read, :read
18
+
19
+ # Offloads `IO.read` to the default thread pool.
16
20
  def read(path)
17
21
  ThreadPool.process { orig_read(path) }
18
22
  end
@@ -1,9 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- # Process patches
4
+ # Process extensions
5
5
  module Process
6
6
  class << self
7
+
8
+ # Watches a forked or spawned process, waiting for it to terminate. If
9
+ # `cmd` is given it is spawned, otherwise the process is forked with the
10
+ # given block.
11
+ #
12
+ # If the operation is interrupted for any reason, the spawned or forked
13
+ # process is killed.
14
+ #
15
+ # @param cmd [String, nil] command to spawn
16
+ # @param &block [Proc] block to fork
17
+ # @return [void]
7
18
  def watch(cmd = nil, &block)
8
19
  terminated = nil
9
20
  pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
@@ -13,6 +24,8 @@ module Polyphony
13
24
  kill_process(pid) unless terminated || pid.nil?
14
25
  end
15
26
 
27
+ private
28
+
16
29
  def kill_process(pid)
17
30
  cancel_after(5) do
18
31
  kill_and_await('TERM', pid)
@@ -7,6 +7,10 @@ require 'hiredis/reader'
7
7
 
8
8
  # Polyphony-based Redis driver
9
9
  class Polyphony::RedisDriver
10
+
11
+ # Connects to a Redis server using the given config.
12
+ #
13
+ # @return [TCPSocket, UNIXSocket, SSLSocket] client connectio
10
14
  def self.connect(config)
11
15
  raise 'unix sockets not supported' if config[:scheme] == 'unix'
12
16
 
@@ -20,28 +24,49 @@ class Polyphony::RedisDriver
20
24
  # connection.connect(config[:host], config[:port], connect_timeout)
21
25
  end
22
26
 
27
+ # Initializes a Redis client connection.
28
+ #
29
+ # @param host [String] hostname
30
+ # @param port [Integer] port number
23
31
  def initialize(host, port)
24
32
  @connection = Polyphony::Net.tcp_connect(host, port)
25
33
  @reader = ::Hiredis::Reader.new
26
34
  end
27
35
 
36
+ # Returns true if connected to server.
37
+ #
38
+ # @return [bool] is connected to server
28
39
  def connected?
29
40
  @connection && !@connection.closed?
30
41
  end
31
42
 
43
+ # Sets a timeout for the connection.
44
+ #
45
+ # @return [void]
32
46
  def timeout=(timeout)
33
47
  # ignore timeout for now
34
48
  end
35
49
 
50
+ # Disconnects from the server.
51
+ #
52
+ # @return [void]
36
53
  def disconnect
37
54
  @connection.close
38
55
  @connection = nil
39
56
  end
40
57
 
58
+ # Sends a command to the server.
59
+ #
60
+ # @param command [Array] Redis command
61
+ # @return [void]
41
62
  def write(command)
42
63
  @connection.write(format_command(command))
43
64
  end
44
65
 
66
+ # Formats a command for sending to server.
67
+ #
68
+ # @param args [Array] command
69
+ # @return [String] formatted command
45
70
  def format_command(args)
46
71
  args = args.flatten
47
72
  (+"*#{args.size}\r\n").tap do |s|
@@ -52,6 +77,9 @@ class Polyphony::RedisDriver
52
77
  end
53
78
  end
54
79
 
80
+ # Reads from the connection, feeding incoming data to the parser.
81
+ #
82
+ # @return [void]
55
83
  def read
56
84
  reply = @reader.gets
57
85
  return reply if reply
@@ -4,14 +4,23 @@ require_relative '../../polyphony'
4
4
  require 'sequel'
5
5
 
6
6
  module Polyphony
7
+
7
8
  # Sequel ConnectionPool that delegates to Polyphony::ResourcePool.
8
9
  class FiberConnectionPool < Sequel::ConnectionPool
10
+
11
+ # Initializes the connection pool.
12
+ #
13
+ # @param db [any] db to connect to
14
+ # @opts [Hash] connection pool options
9
15
  def initialize(db, opts = OPTS)
10
16
  super
11
17
  max_size = Integer(opts[:max_connections] || 4)
12
18
  @pool = Polyphony::ResourcePool.new(limit: max_size) { make_new(:default) }
13
19
  end
14
20
 
21
+ # Holds a connection from the pool, passing it to the given block.
22
+ #
23
+ # @return [any] block's return value
15
24
  def hold(_server = nil)
16
25
  @pool.acquire do |conn|
17
26
  yield conn
@@ -23,16 +32,25 @@ module Polyphony
23
32
  end
24
33
  end
25
34
 
35
+ # Returns the pool's size.
36
+ #
37
+ # @return [Integer] size of pool
26
38
  def size
27
39
  @pool.size
28
40
  end
29
41
 
42
+ # Returns the pool's maximal size.
43
+ #
44
+ # @return [Integer] maximum pool size
30
45
  def max_size
31
46
  @pool.limit
32
47
  end
33
48
 
49
+ # Fills pool and preconnects all db instances in pool.
50
+ #
51
+ # @return [void]
34
52
  def preconnect(_concurrent = false)
35
- @pool.preheat!
53
+ @pool.fill!
36
54
  end
37
55
  end
38
56
 
@@ -1,13 +1,216 @@
1
+ # Kernel extensions
1
2
  module ::Kernel
3
+ # Prints a trace message to `STDOUT`, bypassing the Polyphony backend.
4
+ #
5
+ # @return [void]
2
6
  def trace(*args)
3
7
  STDOUT.orig_write(format_trace(args))
4
8
  end
5
9
 
10
+ # Formats a trace message.
11
+ #
12
+ # @return [String] trace message
6
13
  def format_trace(args)
7
14
  if args.size > 1 && args.first.is_a?(String)
8
15
  format("%s: %p\n", args.shift, args.size == 1 ? args.first : args)
16
+ elsif args.size == 1 && args.first.is_a?(String)
17
+ "#{args.first}\n"
9
18
  else
10
19
  format("%p\n", args.size == 1 ? args.first : args)
11
20
  end
12
21
  end
13
22
  end
23
+
24
+ module Polyphony
25
+
26
+ # Trace provides tools for tracing the activity of the current thread's
27
+ # backend.
28
+ module Trace
29
+ class << self
30
+
31
+ # Starts tracing, emitting events converted to hashes to the given block.
32
+ # If an IO instance is given, events are dumped to it instead.
33
+ #
34
+ # @param io [IO, nil] IO instance
35
+ # @param &block [Proc] event handler block
36
+ # @return [void]
37
+ def start_event_firehose(io = nil, &block)
38
+ Thread.backend.trace_proc = firehose_proc(io, block)
39
+ end
40
+
41
+ private
42
+
43
+ # Returns a firehose proc for the given io and block.
44
+ #
45
+ # @param io [IO, nil] IO instance
46
+ # @param block [Proc] event handler block
47
+ # @return [Proc] firehose proc
48
+ def firehose_proc(io, block)
49
+ if io
50
+ ->(*e) { io.orig_write("#{trace_event_info(e).inspect}\n") }
51
+ elsif block
52
+ ->(*e) { block.(trace_event_info(e)) }
53
+ else
54
+ raise "Please provide an io or a block"
55
+ end
56
+ end
57
+
58
+ # Converts an event (expressed as an array) to a hash.
59
+ #
60
+ # @param e [Array] event as emitted by the backend
61
+ # @return [Hash] event hash
62
+ def trace_event_info(e)
63
+ {
64
+ stamp: Time.now,
65
+ event: e[0]
66
+ }.merge(
67
+ send(:"event_props_#{e[0]}", e)
68
+ )
69
+ end
70
+
71
+ # Returns an event hash for a `:block` event.
72
+ #
73
+ # @param e [Array] event array
74
+ # @return [Hash] event hash
75
+ def event_props_block(e)
76
+ {
77
+ fiber: e[1],
78
+ caller: e[2]
79
+ }
80
+ end
81
+
82
+ # Returns an event hash for a `:enter_poll` event.
83
+ #
84
+ # @param e [Array] event array
85
+ # @return [Hash] event hash
86
+ def event_props_enter_poll(e)
87
+ {}
88
+ end
89
+
90
+ # Returns an event hash for a `:leave_poll` event.
91
+ #
92
+ # @param e [Array] event array
93
+ # @return [Hash] event hash
94
+ def event_props_leave_poll(e)
95
+ {}
96
+ end
97
+
98
+ # Returns an event hash for a `:schedule` event.
99
+ #
100
+ # @param e [Array] event array
101
+ # @return [Hash] event hash
102
+ def event_props_schedule(e)
103
+ {
104
+ fiber: e[1],
105
+ value: e[2],
106
+ caller: e[4],
107
+ source_fiber: Fiber.current
108
+ }
109
+ end
110
+
111
+ # Returns an event hash for a `:spin` event.
112
+ #
113
+ # @param e [Array] event array
114
+ # @return [Hash] event hash
115
+ def event_props_spin(e)
116
+ {
117
+ fiber: e[1],
118
+ caller: e[2],
119
+ source_fiber: Fiber.current
120
+ }
121
+ end
122
+
123
+ # Returns an event hash for a `:terminate` event.
124
+ #
125
+ # @param e [Array] event array
126
+ # @return [Hash] event hash
127
+ def event_props_terminate(e)
128
+ {
129
+ fiber: e[1],
130
+ value: e[2],
131
+ }
132
+ end
133
+
134
+ # Returns an event hash for a `:unblock` event.
135
+ #
136
+ # @param e [Array] event array
137
+ # @return [Hash] event hash
138
+ def event_props_unblock(e)
139
+ {
140
+ fiber: e[1],
141
+ value: e[2],
142
+ caller: e[3],
143
+ }
144
+ end
145
+
146
+ # TODO: work on text formatting of events
147
+ # def format_trace_event_message(e)
148
+ # props = send(:"event_props_#{e[0]}", e).merge(
149
+ # timestamp: format_current_time,
150
+ # event: e[0]
151
+ # )
152
+ # templ = send(:"event_format_#{e[0]}", e)
153
+ # msg = format("%<timestamp>s #{templ}\n", **props)
154
+ # end
155
+
156
+ # def format_current_time
157
+ # Time.now.strftime('%Y-%m-%d %H:%M:%S')
158
+ # end
159
+
160
+ # def generic_event_format
161
+ # '%<event>-12.12s'
162
+ # end
163
+
164
+ # def fiber_event_format
165
+ # "#{generic_event_format} %<fiber>-44.44s"
166
+ # end
167
+
168
+ # def event_format_enter_poll(e)
169
+ # generic_event_format
170
+ # end
171
+
172
+ # def event_format_leave_poll(e)
173
+ # generic_event_format
174
+ # end
175
+
176
+
177
+ # def event_format_schedule(e)
178
+ # "#{fiber_event_format} %<value>-24.24p %<caller>-120.120s <= %<origin_fiber>s"
179
+ # end
180
+
181
+
182
+ # def event_format_unblock(e)
183
+ # "#{fiber_event_format} %<value>-24.24p %<caller>-120.120s"
184
+ # end
185
+
186
+ # def event_format_terminate(e)
187
+ # "#{fiber_event_format} %<value>-24.24p"
188
+ # end
189
+
190
+ # def event_format_block(e)
191
+ # "#{fiber_event_format} #{' ' * 24} %<caller>-120.120s"
192
+ # end
193
+
194
+
195
+ # def event_format_spin(e)
196
+ # "#{fiber_event_format} #{' ' * 24} %<caller>-120.120s <= %<origin_fiber>s"
197
+ # end
198
+
199
+ # def fibe_repr(fiber)
200
+ # format("%-6x %-20.20s %-10.10s", fiber.object_id, fiber.tag, "(#{fiber.state})")
201
+ # end
202
+
203
+ # def fiber_compact_repr(fiber)
204
+ # if fiber.tag
205
+ # format("%-6x %-.20s %-.10s", fiber.object_id, fiber.tag, "(#{fiber.state})")
206
+ # else
207
+ # format("%-6x %-.10s", fiber.object_id, "(#{fiber.state})")
208
+ # end
209
+ # end
210
+
211
+ # def caller_repr(c)
212
+ # c.map { |i| i.gsub('/home/sharon/repo/polyphony/lib/polyphony', '') }.join(' ')
213
+ # end
214
+ end
215
+ end
216
+ end
@@ -1,15 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- # Common exception class for interrupting fibers. These exceptions allow
5
- # control of fibers. BaseException exceptions can encapsulate a value and thus
6
- # provide a way to interrupt long-running blocking operations while still
7
- # passing a value back to the call site. BaseException exceptions can also
8
- # references a cancel scope in order to allow correct bubbling of exceptions
9
- # through nested cancel scopes.
4
+
5
+ # Base exception class for interrupting fibers. These exceptions allow control
6
+ # of fibers. BaseException exceptions can encapsulate a value and thus provide
7
+ # a way to interrupt long-running blocking operations while still passing a
8
+ # value back to the call site. BaseException exceptions can also references a
9
+ # cancel scope in order to allow correct bubbling of exceptions through nested
10
+ # cancel scopes.
10
11
  class BaseException < ::Exception
12
+
13
+ # Exception value, used mainly for `MoveOn` exceptions.
11
14
  attr_reader :value
12
15
 
16
+ # Initializes the exception, setting the caller and the value.
17
+ #
18
+ # @param value [any] Exception value
19
+ # @return [void]
13
20
  def initialize(value = nil)
14
21
  @caller_backtrace = caller
15
22
  @value = value
@@ -33,10 +40,18 @@ module Polyphony
33
40
 
34
41
  # Interjection is used to run arbitrary code on arbitrary fibers at any point
35
42
  class Interjection < BaseException
43
+
44
+ # Initializes an Interjection with the given proc.
45
+ #
46
+ # @param proc [Proc] interjection proc
47
+ # @return [void]
36
48
  def initialize(proc)
37
49
  @proc = proc
38
50
  end
39
51
 
52
+ # Invokes the exception by calling the associated proc.
53
+ #
54
+ # @return [void]
40
55
  def invoke
41
56
  @proc.call
42
57
  end