polyphony 0.79 → 0.81.1
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/CHANGELOG.md +19 -0
- data/Gemfile.lock +2 -1
- data/examples/core/raw_buffer_test.rb +8 -0
- data/examples/core/zlib_stream.rb +16 -0
- data/ext/polyphony/backend_common.c +2 -1
- data/ext/polyphony/backend_common.h +7 -2
- data/ext/polyphony/backend_io_uring.c +69 -30
- data/ext/polyphony/polyphony.c +8 -0
- data/ext/polyphony/polyphony.h +11 -0
- 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 +129 -72
- 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 +238 -57
- 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 +66 -7
- 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_resource_pool.rb +1 -1
- data/test/test_throttler.rb +0 -6
- data/test/test_trace.rb +87 -0
- metadata +10 -8
- data/lib/polyphony/core/channel.rb +0 -15
data/lib/polyphony/core/debug.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
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)
|
@@ -15,14 +22,29 @@ module ::Kernel
|
|
15
22
|
end
|
16
23
|
|
17
24
|
module Polyphony
|
25
|
+
|
26
|
+
# Trace provides tools for tracing the activity of the current thread's
|
27
|
+
# backend.
|
18
28
|
module Trace
|
19
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]
|
20
37
|
def start_event_firehose(io = nil, &block)
|
21
38
|
Thread.backend.trace_proc = firehose_proc(io, block)
|
22
39
|
end
|
23
40
|
|
24
41
|
private
|
25
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
|
26
48
|
def firehose_proc(io, block)
|
27
49
|
if io
|
28
50
|
->(*e) { io.orig_write("#{trace_event_info(e).inspect}\n") }
|
@@ -33,53 +55,50 @@ module Polyphony
|
|
33
55
|
end
|
34
56
|
end
|
35
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
|
36
62
|
def trace_event_info(e)
|
37
63
|
{
|
38
|
-
stamp:
|
64
|
+
stamp: Time.now,
|
39
65
|
event: e[0]
|
40
66
|
}.merge(
|
41
67
|
send(:"event_props_#{e[0]}", e)
|
42
68
|
)
|
43
69
|
end
|
44
|
-
|
45
|
-
def format_trace_event_message(e)
|
46
|
-
props = send(:"event_props_#{e[0]}", e).merge(
|
47
|
-
timestamp: format_current_time,
|
48
|
-
event: e[0]
|
49
|
-
)
|
50
|
-
# templ = send(:"event_format_#{e[0]}", e)
|
51
|
-
|
52
|
-
# msg = format("%<timestamp>s #{templ}\n", **props)
|
53
|
-
end
|
54
|
-
|
55
|
-
def format_current_time
|
56
|
-
Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
57
|
-
end
|
58
70
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
def
|
64
|
-
|
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
|
+
}
|
65
80
|
end
|
66
81
|
|
82
|
+
# Returns an event hash for a `:enter_poll` event.
|
83
|
+
#
|
84
|
+
# @param e [Array] event array
|
85
|
+
# @return [Hash] event hash
|
67
86
|
def event_props_enter_poll(e)
|
68
87
|
{}
|
69
88
|
end
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
90
|
+
# Returns an event hash for a `:leave_poll` event.
|
91
|
+
#
|
92
|
+
# @param e [Array] event array
|
93
|
+
# @return [Hash] event hash
|
75
94
|
def event_props_leave_poll(e)
|
76
95
|
{}
|
77
96
|
end
|
78
97
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
98
|
+
# Returns an event hash for a `:schedule` event.
|
99
|
+
#
|
100
|
+
# @param e [Array] event array
|
101
|
+
# @return [Hash] event hash
|
83
102
|
def event_props_schedule(e)
|
84
103
|
{
|
85
104
|
fiber: e[1],
|
@@ -89,22 +108,22 @@ module Polyphony
|
|
89
108
|
}
|
90
109
|
end
|
91
110
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
def
|
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)
|
97
116
|
{
|
98
117
|
fiber: e[1],
|
99
|
-
|
100
|
-
|
118
|
+
caller: e[2],
|
119
|
+
source_fiber: Fiber.current
|
101
120
|
}
|
102
121
|
end
|
103
122
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
123
|
+
# Returns an event hash for a `:terminate` event.
|
124
|
+
#
|
125
|
+
# @param e [Array] event array
|
126
|
+
# @return [Hash] event hash
|
108
127
|
def event_props_terminate(e)
|
109
128
|
{
|
110
129
|
fiber: e[1],
|
@@ -112,48 +131,86 @@ module Polyphony
|
|
112
131
|
}
|
113
132
|
end
|
114
133
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def
|
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)
|
120
139
|
{
|
121
140
|
fiber: e[1],
|
122
|
-
|
141
|
+
value: e[2],
|
142
|
+
caller: e[3],
|
123
143
|
}
|
124
144
|
end
|
125
145
|
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
129
155
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
caller: e[2],
|
134
|
-
source_fiber: Fiber.current
|
135
|
-
}
|
136
|
-
end
|
156
|
+
# def format_current_time
|
157
|
+
# Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
158
|
+
# end
|
137
159
|
|
138
|
-
def
|
139
|
-
|
140
|
-
end
|
160
|
+
# def generic_event_format
|
161
|
+
# '%<event>-12.12s'
|
162
|
+
# end
|
141
163
|
|
142
|
-
def
|
143
|
-
|
144
|
-
end
|
164
|
+
# def fiber_event_format
|
165
|
+
# "#{generic_event_format} %<fiber>-44.44s"
|
166
|
+
# end
|
145
167
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
else
|
150
|
-
format("%-6x %-.10s", fiber.object_id, "(#{fiber.state})")
|
151
|
-
end
|
152
|
-
end
|
168
|
+
# def event_format_enter_poll(e)
|
169
|
+
# generic_event_format
|
170
|
+
# end
|
153
171
|
|
154
|
-
def
|
155
|
-
|
156
|
-
end
|
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
|
157
214
|
end
|
158
215
|
end
|
159
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
|
@@ -3,8 +3,16 @@
|
|
3
3
|
require_relative './throttler'
|
4
4
|
|
5
5
|
module Polyphony
|
6
|
-
|
6
|
+
|
7
|
+
# Global API methods to be included in `::Object`
|
7
8
|
module GlobalAPI
|
9
|
+
|
10
|
+
# Spins up a fiber that will run the given block after sleeping for the
|
11
|
+
# given delay.
|
12
|
+
#
|
13
|
+
# @param delay [Number] delay in seconds before running the given block
|
14
|
+
# @param &block [Proc] block to run
|
15
|
+
# @return [Fiber] spun fiber
|
8
16
|
def after(interval, &block)
|
9
17
|
spin do
|
10
18
|
sleep interval
|
@@ -12,64 +20,88 @@ module Polyphony
|
|
12
20
|
end
|
13
21
|
end
|
14
22
|
|
23
|
+
# call-seq:
|
24
|
+
# cancel_after(interval) { ... }
|
25
|
+
# cancel_after(interval, with_exception: exception) { ... }
|
26
|
+
# cancel_after(interval, with_exception: [klass, message]) { ... }
|
27
|
+
# cancel_after(interval) { |timeout| ... }
|
28
|
+
# cancel_after(interval, with_exception: exception) { |timeout| ... }
|
29
|
+
# cancel_after(interval, with_exception: [klass, message]) { |timeout| ... }
|
30
|
+
#
|
31
|
+
# Runs the given block after setting up a cancellation timer for
|
32
|
+
# cancellation. If the cancellation timer elapses, the execution will be
|
33
|
+
# interrupted with an exception defaulting to `Polyphony::Cancel`.
|
34
|
+
#
|
35
|
+
# This method should be used when a timeout should cause an exception to be
|
36
|
+
# propagated down the call stack or up the fiber tree.
|
37
|
+
#
|
38
|
+
# Example of normal use:
|
39
|
+
#
|
40
|
+
# def read_from_io_with_timeout(io)
|
41
|
+
# cancel_after(10) { io.read }
|
42
|
+
# rescue Polyphony::Cancel
|
43
|
+
# nil
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# The timeout period can be reset by passing a block that takes a single
|
47
|
+
# argument. The block will be provided with the canceller fiber. To reset
|
48
|
+
# the timeout, use `Fiber#reset`, as shown in the following example:
|
49
|
+
#
|
50
|
+
# cancel_after(10) do |timeout|
|
51
|
+
# loop do
|
52
|
+
# msg = socket.gets
|
53
|
+
# timeout.reset
|
54
|
+
# handle_msg(msg)
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# @param interval [Number] timout in seconds
|
59
|
+
# @param with_exception: [Class, Exception] exception or exception class
|
60
|
+
# @param &block [Proc] block to execute
|
61
|
+
# @return [any] block's return value
|
15
62
|
def cancel_after(interval, with_exception: Polyphony::Cancel, &block)
|
16
|
-
if
|
17
|
-
|
18
|
-
elsif block.arity > 0
|
19
|
-
cancel_after_with_block(Fiber.current, interval, with_exception, &block)
|
63
|
+
if block.arity > 0
|
64
|
+
cancel_after_with_optional_reset(interval, with_exception, &block)
|
20
65
|
else
|
21
66
|
Polyphony.backend_timeout(interval, with_exception, &block)
|
22
67
|
end
|
23
68
|
end
|
24
69
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
fiber.schedule exception
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def cancel_after_with_block(fiber, interval, with_exception, &block)
|
35
|
-
canceller = cancel_after_blockless_canceller(fiber, interval, with_exception)
|
36
|
-
block.call(canceller)
|
37
|
-
ensure
|
38
|
-
canceller.stop
|
39
|
-
end
|
40
|
-
|
41
|
-
def cancel_exception(exception)
|
42
|
-
case exception
|
43
|
-
when Class then exception.new
|
44
|
-
when Array then exception[0].new(exception[1])
|
45
|
-
else RuntimeError.new(exception)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
70
|
+
# Spins up a new fiber.
|
71
|
+
#
|
72
|
+
# @param tag [any] optional tag for the new fiber
|
73
|
+
# @param &block [Proc] fiber block
|
74
|
+
# @return [Fiber] new fiber
|
49
75
|
def spin(tag = nil, &block)
|
50
76
|
Fiber.current.spin(tag, caller, &block)
|
51
77
|
end
|
52
78
|
|
79
|
+
# Spins up a new fiber, running the given block inside an infinite loop. If
|
80
|
+
# `rate:` or `interval:` parameters are given, the loop is throttled
|
81
|
+
# accordingly.
|
82
|
+
#
|
83
|
+
# @param tag [any] optional tag for the new fiber
|
84
|
+
# @param rate: [Number, nil] loop rate (times per second)
|
85
|
+
# @param interval: [Number, nil] interval between consecutive iterations in seconds
|
86
|
+
# @param &block [Proc] code to run
|
87
|
+
# @return [Fiber] new fiber
|
53
88
|
def spin_loop(tag = nil, rate: nil, interval: nil, &block)
|
54
89
|
if rate || interval
|
55
90
|
Fiber.current.spin(tag, caller) do
|
56
91
|
throttled_loop(rate: rate, interval: interval, &block)
|
57
92
|
end
|
58
93
|
else
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def spin_looped_block(tag, caller, block)
|
64
|
-
Fiber.current.spin(tag, caller) do
|
65
|
-
block.call while true
|
66
|
-
rescue LocalJumpError, StopIteration
|
67
|
-
# break called or StopIteration raised
|
94
|
+
spin_loop_without_throttling(tag, caller, block)
|
68
95
|
end
|
69
96
|
end
|
70
97
|
|
71
|
-
|
72
|
-
|
98
|
+
# Runs the given code, then waits for any child fibers of the current fibers
|
99
|
+
# to terminate.
|
100
|
+
#
|
101
|
+
# @param &block [Proc] code to run
|
102
|
+
# @return [any] given block's return value
|
103
|
+
def spin_scope(&block)
|
104
|
+
raise unless block
|
73
105
|
|
74
106
|
spin do
|
75
107
|
result = yield
|
@@ -78,61 +110,121 @@ module Polyphony
|
|
78
110
|
end.await
|
79
111
|
end
|
80
112
|
|
113
|
+
# Runs the given block in an infinite loop with a regular interval between
|
114
|
+
# consecutive iterations.
|
115
|
+
#
|
116
|
+
# @param interval [Number] interval between consecutive iterations in seconds
|
117
|
+
# @param &block [Proc] block to run
|
118
|
+
# @return [void]
|
81
119
|
def every(interval, &block)
|
82
120
|
Polyphony.backend_timer_loop(interval, &block)
|
83
121
|
end
|
84
122
|
|
123
|
+
# call-seq:
|
124
|
+
# move_on_after(interval) { ... }
|
125
|
+
# move_on_after(interval, with_value: value) { ... }
|
126
|
+
# move_on_after(interval) { |canceller| ... }
|
127
|
+
# move_on_after(interval, with_value: value) { |canceller| ... }
|
128
|
+
#
|
129
|
+
# Runs the given block after setting up a cancellation timer for
|
130
|
+
# cancellation. If the cancellation timer elapses, the execution will be
|
131
|
+
# interrupted with a `Polyphony::MoveOn` exception, which will be rescued,
|
132
|
+
# and with cause the operation to return the given value.
|
133
|
+
#
|
134
|
+
# This method should be used when a timeout is to be handled locally,
|
135
|
+
# without generating an exception that is to propagated down the call stack
|
136
|
+
# or up the fiber tree.
|
137
|
+
#
|
138
|
+
# Example of normal use:
|
139
|
+
#
|
140
|
+
# move_on_after(10) {
|
141
|
+
# sleep 60
|
142
|
+
# 42
|
143
|
+
# } #=> nil
|
144
|
+
#
|
145
|
+
# move_on_after(10, with_value: :oops) {
|
146
|
+
# sleep 60
|
147
|
+
# 42
|
148
|
+
# } #=> :oops
|
149
|
+
#
|
150
|
+
# The timeout period can be reset by passing a block that takes a single
|
151
|
+
# argument. The block will be provided with the canceller fiber. To reset
|
152
|
+
# the timeout, use `Fiber#reset`, as shown in the following example:
|
153
|
+
#
|
154
|
+
# move_on_after(10) do |timeout|
|
155
|
+
# loop do
|
156
|
+
# msg = socket.gets
|
157
|
+
# timeout.reset
|
158
|
+
# handle_msg(msg)
|
159
|
+
# end
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# @param interval [Number] timout in seconds
|
163
|
+
# @param with_value: [any] return value in case of timeout
|
164
|
+
# @param &block [Proc] block to execute
|
165
|
+
# @return [any] block's return value
|
85
166
|
def move_on_after(interval, with_value: nil, &block)
|
86
|
-
if
|
87
|
-
|
88
|
-
elsif block.arity > 0
|
89
|
-
move_on_after_with_block(Fiber.current, interval, with_value, &block)
|
167
|
+
if block.arity > 0
|
168
|
+
move_on_after_with_optional_reset(interval, with_value, &block)
|
90
169
|
else
|
91
170
|
Polyphony.backend_timeout(interval, nil, with_value, &block)
|
92
171
|
end
|
93
172
|
end
|
94
173
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def move_on_after_with_block(fiber, interval, with_value, &block)
|
103
|
-
canceller = spin do
|
104
|
-
sleep interval
|
105
|
-
fiber.schedule Polyphony::MoveOn.new(with_value)
|
106
|
-
end
|
107
|
-
block.call(canceller)
|
108
|
-
rescue Polyphony::MoveOn => e
|
109
|
-
e.value
|
110
|
-
ensure
|
111
|
-
canceller.stop
|
112
|
-
end
|
113
|
-
|
174
|
+
# Returns the first message from the current fiber's mailbox. If the mailbox
|
175
|
+
# is empty, blocks until a message is available.
|
176
|
+
#
|
177
|
+
# @return [any] received message
|
114
178
|
def receive
|
115
179
|
Fiber.current.receive
|
116
180
|
end
|
117
181
|
|
182
|
+
# Returns all messages currently pending on the current fiber's mailbox.
|
183
|
+
#
|
184
|
+
# @return [Array] array of received messages
|
118
185
|
def receive_all_pending
|
119
186
|
Fiber.current.receive_all_pending
|
120
187
|
end
|
121
188
|
|
189
|
+
# Supervises the current fiber's children. See `Fiber#supervise` for
|
190
|
+
# options.
|
191
|
+
#
|
192
|
+
# @param *args [Array] positional parameters
|
193
|
+
# @param **opts [Hash] named parameters
|
194
|
+
# @param &block [Proc] given block
|
195
|
+
# @return [void]
|
122
196
|
def supervise(*args, **opts, &block)
|
123
197
|
Fiber.current.supervise(*args, **opts, &block)
|
124
198
|
end
|
125
199
|
|
200
|
+
# Sleeps for the given duration. If the duration is `nil`, sleeps
|
201
|
+
# indefinitely.
|
202
|
+
#
|
203
|
+
# @param duration [Number, nil] duration
|
204
|
+
# @return [void]
|
126
205
|
def sleep(duration = nil)
|
127
|
-
|
128
|
-
|
129
|
-
Polyphony.backend_sleep duration
|
130
|
-
end
|
131
|
-
|
132
|
-
def sleep_forever
|
133
|
-
Polyphony.backend_wait_event(true)
|
206
|
+
duration ?
|
207
|
+
Polyphony.backend_sleep(duration) : Polyphony.backend_wait_event(true)
|
134
208
|
end
|
135
209
|
|
210
|
+
# call-seq:
|
211
|
+
# throttled_loop(rate) { ... }
|
212
|
+
# throttled_loop(interval: value) { ... }
|
213
|
+
# throttled_loop(rate: value) { ... }
|
214
|
+
# throttled_loop(rate, count: value) { ... }
|
215
|
+
#
|
216
|
+
# Starts a throttled loop with the given rate. If `count:` is given, the
|
217
|
+
# loop is run for the given number of times. Otherwise, the loop is
|
218
|
+
# infinite. The loop rate (times per second) can be given as the rate
|
219
|
+
# parameter. The throttling can also be controlled by providing an
|
220
|
+
# `interval:` or `rate:` named parameter.
|
221
|
+
#
|
222
|
+
# @param rate [Number, nil] loop rate (times per second)
|
223
|
+
# @param rate: [Number] loop rate (times per second)
|
224
|
+
# @param interval: [Number] loop interval in seconds
|
225
|
+
# @param count: [Number, nil] number of iterations (nil for infinite)
|
226
|
+
# @param &block [Proc] code to run
|
227
|
+
# @return [void]
|
136
228
|
def throttled_loop(rate = nil, **opts, &block)
|
137
229
|
throttler = Polyphony::Throttler.new(rate || opts)
|
138
230
|
if opts[:count]
|
@@ -144,10 +236,73 @@ module Polyphony
|
|
144
236
|
end
|
145
237
|
rescue LocalJumpError, StopIteration
|
146
238
|
# break called or StopIteration raised
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
# Helper method for performing a `cancel_after` with optional reset.
|
244
|
+
#
|
245
|
+
# @param interval [Number] timeout interval in seconds
|
246
|
+
# @param exception [Exception, Class, Array<class, message>] exception spec
|
247
|
+
# @param &block [Proc] block to run
|
248
|
+
# @return [any] block's return value
|
249
|
+
def cancel_after_with_optional_reset(interval, exception, &block)
|
250
|
+
canceller = spin do
|
251
|
+
sleep interval
|
252
|
+
exception = cancel_exception(exception)
|
253
|
+
exception.raising_fiber = Fiber.current
|
254
|
+
fiber.cancel(exception).await
|
255
|
+
end
|
256
|
+
block.call(canceller)
|
147
257
|
ensure
|
148
|
-
|
258
|
+
canceller.stop
|
259
|
+
end
|
260
|
+
|
261
|
+
# Converts the given exception spec to an exception instance.
|
262
|
+
#
|
263
|
+
# @param exception [Exception, Class, Array<class, message>] exception spec
|
264
|
+
# @return [Exception] exception instance
|
265
|
+
def cancel_exception(exception)
|
266
|
+
case exception
|
267
|
+
when Class then exception.new
|
268
|
+
when Array then exception[0].new(exception[1])
|
269
|
+
else RuntimeError.new(exception)
|
270
|
+
end
|
149
271
|
end
|
272
|
+
|
273
|
+
# Helper method for performing `#spin_loop` without throttling. Spins up a
|
274
|
+
# new fiber in which to run the loop.
|
275
|
+
#
|
276
|
+
# @param tag [any] new fiber's tag
|
277
|
+
# @param caller [Array<String>] caller info
|
278
|
+
# @param block [Proc] code to run
|
279
|
+
# @return [void]
|
280
|
+
def spin_loop_without_throttling(tag, caller, block)
|
281
|
+
Fiber.current.spin(tag, caller) do
|
282
|
+
block.call while true
|
283
|
+
rescue LocalJumpError, StopIteration
|
284
|
+
# break called or StopIteration raised
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# Helper method for performing `#move_on_after` with optional reset.
|
289
|
+
#
|
290
|
+
# @param interval [Number] timeout interval in seconds
|
291
|
+
# @param value [any] return value in case of timeout
|
292
|
+
# @param &block [Proc] code to run
|
293
|
+
# @return [any] return value of given block or timeout value
|
294
|
+
def move_on_after_with_optional_reset(interval, value, &block)
|
295
|
+
fiber = Fiber.current
|
296
|
+
canceller = spin do
|
297
|
+
sleep interval
|
298
|
+
fiber.move_on(value).await
|
299
|
+
end
|
300
|
+
block.call(canceller)
|
301
|
+
rescue Polyphony::MoveOn => e
|
302
|
+
e.value
|
303
|
+
ensure
|
304
|
+
canceller.stop
|
305
|
+
end
|
306
|
+
|
150
307
|
end
|
151
308
|
end
|
152
|
-
|
153
|
-
Object.include Polyphony::GlobalAPI
|