polyphony 0.99.5 → 0.99.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,295 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './throttler'
4
-
5
- module Polyphony
6
-
7
- # Global API methods to be included in `::Object`
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 interval [Number] delay in seconds before running the given block
14
- # @return [Fiber] spun fiber
15
- def after(interval, &block)
16
- spin do
17
- sleep interval
18
- block.()
19
- end
20
- end
21
-
22
- # Runs the given block after setting up a cancellation timer for
23
- # cancellation. If the cancellation timer elapses, the execution will be
24
- # interrupted with an exception defaulting to `Polyphony::Cancel`.
25
- #
26
- # This method should be used when a timeout should cause an exception to be
27
- # propagated down the call stack or up the fiber tree.
28
- #
29
- # Example of normal use:
30
- #
31
- # def read_from_io_with_timeout(io)
32
- # cancel_after(10) { io.read }
33
- # rescue Polyphony::Cancel
34
- # nil
35
- # end
36
- #
37
- # The timeout period can be reset by passing a block that takes a single
38
- # argument. The block will be provided with the canceller fiber. To reset
39
- # the timeout, use `Fiber#reset`, as shown in the following example:
40
- #
41
- # cancel_after(10) do |timeout|
42
- # loop do
43
- # msg = socket.gets
44
- # timeout.reset
45
- # handle_msg(msg)
46
- # end
47
- # end
48
- #
49
- # @overload cancel_after(interval)
50
- # @param interval [Number] timout in seconds
51
- # @yield [Fiber] timeout fiber
52
- # @return [any] block's return value
53
- # @overload cancel_after(interval, with_exception: exception)
54
- # @param interval [Number] timout in seconds
55
- # @param with_exception [Class, Exception] exception or exception class
56
- # @yield [Fiber] timeout fiber
57
- # @return [any] block's return value
58
- # @overload cancel_after(interval, with_exception: [klass, message])
59
- # @param interval [Number] timout in seconds
60
- # @param with_exception [Array] array containing class and message to use as exception
61
- # @yield [Fiber] timeout fiber
62
- # @return [any] block's return value
63
- def cancel_after(interval, with_exception: Polyphony::Cancel, &block)
64
- if block.arity > 0
65
- cancel_after_with_optional_reset(interval, with_exception, &block)
66
- else
67
- Polyphony.backend_timeout(interval, with_exception, &block)
68
- end
69
- end
70
-
71
- # Spins up a new fiber.
72
- #
73
- # @param tag [any] optional tag for the new fiber
74
- # @return [Fiber] new fiber
75
- def spin(tag = nil, &block)
76
- Fiber.current.spin(tag, caller, &block)
77
- end
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
- # @return [Fiber] new fiber
87
- def spin_loop(tag = nil, rate: nil, interval: nil, &block)
88
- if rate || interval
89
- Fiber.current.spin(tag, caller) do
90
- throttled_loop(rate: rate, interval: interval, &block)
91
- end
92
- else
93
- spin_loop_without_throttling(tag, caller, block)
94
- end
95
- end
96
-
97
- # Runs the given code, then waits for any child fibers of the current fibers
98
- # to terminate.
99
- #
100
- # @return [any] given block's return value
101
- def spin_scope(&block)
102
- raise unless block
103
-
104
- spin do
105
- result = yield
106
- Fiber.current.await_all_children
107
- result
108
- end.await
109
- end
110
-
111
- # Runs the given block in an infinite loop with a regular interval between
112
- # consecutive iterations.
113
- #
114
- # @param interval [Number] interval between consecutive iterations in seconds
115
- # @return [void]
116
- def every(interval, &block)
117
- Polyphony.backend_timer_loop(interval, &block)
118
- end
119
-
120
- # Runs the given block after setting up a cancellation timer for
121
- # cancellation. If the cancellation timer elapses, the execution will be
122
- # interrupted with a `Polyphony::MoveOn` exception, which will be rescued,
123
- # and with cause the operation to return the given value.
124
- #
125
- # This method should be used when a timeout is to be handled locally,
126
- # without generating an exception that is to propagated down the call stack
127
- # or up the fiber tree.
128
- #
129
- # Example of normal use:
130
- #
131
- # move_on_after(10) {
132
- # sleep 60
133
- # 42
134
- # } #=> nil
135
- #
136
- # move_on_after(10, with_value: :oops) {
137
- # sleep 60
138
- # 42
139
- # } #=> :oops
140
- #
141
- # The timeout period can be reset by passing a block that takes a single
142
- # argument. The block will be provided with the canceller fiber. To reset
143
- # the timeout, use `Fiber#reset`, as shown in the following example:
144
- #
145
- # move_on_after(10) do |timeout|
146
- # loop do
147
- # msg = socket.gets
148
- # timeout.reset
149
- # handle_msg(msg)
150
- # end
151
- # end
152
- #
153
- # @overload move_on_after(interval) { ... }
154
- # @param interval [Number] timout in seconds
155
- # @yield [Fiber] timeout fiber
156
- # @return [any] block's return value
157
- # @overload move_on_after(interval, with_value: value) { ... }
158
- # @param interval [Number] timout in seconds
159
- # @param with_value [any] return value in case of timeout
160
- # @yield [Fiber] timeout fiber
161
- # @return [any] block's return value
162
- def move_on_after(interval, with_value: nil, &block)
163
- if block.arity > 0
164
- move_on_after_with_optional_reset(interval, with_value, &block)
165
- else
166
- Polyphony.backend_timeout(interval, nil, with_value, &block)
167
- end
168
- end
169
-
170
- # Returns the first message from the current fiber's mailbox. If the mailbox
171
- # is empty, blocks until a message is available.
172
- #
173
- # @return [any] received message
174
- def receive
175
- Fiber.current.receive
176
- end
177
-
178
- # Returns all messages currently pending on the current fiber's mailbox.
179
- #
180
- # @return [Array] array of received messages
181
- def receive_all_pending
182
- Fiber.current.receive_all_pending
183
- end
184
-
185
- # Supervises the current fiber's children. See `Fiber#supervise` for
186
- # options.
187
- #
188
- # @param args [Array] positional parameters
189
- # @param opts [Hash] named parameters
190
- # @return [void]
191
- def supervise(*args, **opts, &block)
192
- Fiber.current.supervise(*args, **opts, &block)
193
- end
194
-
195
- # Sleeps for the given duration. If the duration is `nil`, sleeps
196
- # indefinitely.
197
- #
198
- # @param duration [Number, nil] duration
199
- # @return [void]
200
- def sleep(duration = nil)
201
- duration ?
202
- Polyphony.backend_sleep(duration) : Polyphony.backend_wait_event(true)
203
- end
204
-
205
- # Starts a throttled loop with the given rate. If `count:` is given, the
206
- # loop is run for the given number of times. Otherwise, the loop is
207
- # infinite. The loop rate (times per second) can be given as the rate
208
- # parameter. The throttling can also be controlled by providing an
209
- # `interval:` or `rate:` named parameter.
210
- #
211
- # @param rate [Number, nil] loop rate (times per second)
212
- # @option opts [Number] :rate loop rate (times per second)
213
- # @option opts [Number] :interval loop interval in seconds
214
- # @option opts [Number] :count number of iterations (nil for infinite)
215
- # @return [void]
216
- def throttled_loop(rate = nil, **opts, &block)
217
- throttler = Polyphony::Throttler.new(rate || opts)
218
- if opts[:count]
219
- opts[:count].times { |_i| throttler.(&block) }
220
- else
221
- while true
222
- throttler.(&block)
223
- end
224
- end
225
- rescue LocalJumpError, StopIteration
226
- # break called or StopIteration raised
227
- end
228
-
229
- private
230
-
231
- # Helper method for performing a `cancel_after` with optional reset.
232
- #
233
- # @param interval [Number] timeout interval in seconds
234
- # @param exception [Exception, Class, Array<class, message>] exception spec
235
- # @return [any] block's return value
236
- def cancel_after_with_optional_reset(interval, exception, &block)
237
- fiber = Fiber.current
238
- canceller = spin do
239
- Polyphony.backend_sleep(interval)
240
- exception = cancel_exception(exception)
241
- exception.raising_fiber = Fiber.current
242
- fiber.cancel(exception)
243
- end
244
- block.call(canceller)
245
- ensure
246
- canceller.stop
247
- end
248
-
249
- # Converts the given exception spec to an exception instance.
250
- #
251
- # @param exception [Exception, Class, Array<class, message>] exception spec
252
- # @return [Exception] exception instance
253
- def cancel_exception(exception)
254
- case exception
255
- when Class then exception.new
256
- when Array then exception[0].new(exception[1])
257
- else RuntimeError.new(exception)
258
- end
259
- end
260
-
261
- # Helper method for performing `#spin_loop` without throttling. Spins up a
262
- # new fiber in which to run the loop.
263
- #
264
- # @param tag [any] new fiber's tag
265
- # @param caller [Array<String>] caller info
266
- # @param block [Proc] code to run
267
- # @return [void]
268
- def spin_loop_without_throttling(tag, caller, block)
269
- Fiber.current.spin(tag, caller) do
270
- block.call while true
271
- rescue LocalJumpError, StopIteration
272
- # break called or StopIteration raised
273
- end
274
- end
275
-
276
- # Helper method for performing `#move_on_after` with optional reset.
277
- #
278
- # @param interval [Number] timeout interval in seconds
279
- # @param value [any] return value in case of timeout
280
- # @return [any] return value of given block or timeout value
281
- def move_on_after_with_optional_reset(interval, value, &block)
282
- fiber = Fiber.current
283
- canceller = spin do
284
- sleep interval
285
- fiber.move_on(value)
286
- end
287
- block.call(canceller)
288
- rescue Polyphony::MoveOn => e
289
- e.value
290
- ensure
291
- canceller.stop
292
- end
293
-
294
- end
295
- end
File without changes
File without changes
File without changes