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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile.lock +2 -1
- data/examples/core/pingpong.rb +7 -4
- data/examples/core/zlib_stream.rb +15 -0
- data/ext/polyphony/backend_common.c +16 -8
- data/ext/polyphony/backend_common.h +8 -3
- data/ext/polyphony/backend_io_uring.c +19 -3
- data/ext/polyphony/backend_libev.c +33 -17
- data/ext/polyphony/fiber.c +28 -28
- data/ext/polyphony/polyphony.c +1 -8
- data/ext/polyphony/polyphony.h +11 -8
- data/ext/polyphony/queue.c +82 -6
- data/ext/polyphony/thread.c +6 -2
- data/lib/polyphony/adapters/fs.rb +4 -0
- data/lib/polyphony/adapters/process.rb +14 -1
- data/lib/polyphony/adapters/redis.rb +28 -0
- data/lib/polyphony/adapters/sequel.rb +19 -1
- data/lib/polyphony/core/debug.rb +203 -0
- data/lib/polyphony/core/exceptions.rb +21 -6
- data/lib/polyphony/core/global_api.rb +228 -73
- data/lib/polyphony/core/resource_pool.rb +65 -20
- data/lib/polyphony/core/sync.rb +57 -12
- data/lib/polyphony/core/thread_pool.rb +42 -5
- data/lib/polyphony/core/throttler.rb +21 -5
- data/lib/polyphony/core/timer.rb +125 -1
- data/lib/polyphony/extensions/exception.rb +36 -6
- data/lib/polyphony/extensions/fiber.rb +244 -61
- data/lib/polyphony/extensions/io.rb +4 -2
- data/lib/polyphony/extensions/kernel.rb +9 -4
- data/lib/polyphony/extensions/object.rb +8 -0
- data/lib/polyphony/extensions/openssl.rb +3 -1
- data/lib/polyphony/extensions/socket.rb +458 -39
- data/lib/polyphony/extensions/thread.rb +108 -43
- data/lib/polyphony/extensions/timeout.rb +12 -1
- data/lib/polyphony/extensions.rb +1 -0
- data/lib/polyphony/net.rb +59 -0
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +0 -2
- data/test/test_backend.rb +6 -2
- data/test/test_global_api.rb +0 -23
- data/test/test_io.rb +7 -7
- data/test/test_queue.rb +103 -1
- data/test/test_resource_pool.rb +1 -1
- data/test/test_signal.rb +15 -15
- data/test/test_supervise.rb +27 -0
- data/test/test_thread.rb +1 -1
- data/test/test_throttler.rb +0 -6
- data/test/test_trace.rb +189 -24
- metadata +9 -8
- data/lib/polyphony/core/channel.rb +0 -15
data/ext/polyphony/queue.c
CHANGED
@@ -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
|
123
|
-
|
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
|
-
|
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,
|
264
|
-
rb_define_method(cQueue, "pop", Queue_shift,
|
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, "
|
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
|
}
|
data/ext/polyphony/thread.c
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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.
|
53
|
+
@pool.fill!
|
36
54
|
end
|
37
55
|
end
|
38
56
|
|
data/lib/polyphony/core/debug.rb
CHANGED
@@ -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
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# through nested
|
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
|