polyphony 0.99.5 → 1.0

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