async 1.28.6 → 1.29.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/async/debug/monitor.rb +8 -10
- data/lib/async/debug/selector.rb +8 -15
- data/lib/async/node.rb +50 -22
- data/lib/async/reactor.rb +13 -27
- data/lib/async/scheduler.rb +2 -0
- data/lib/async/task.rb +8 -13
- data/lib/async/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94443a93442232e8189364cd931084a0cf188a9adea3fe91287f192fed44fc2e
|
4
|
+
data.tar.gz: cec386ffc8948123aeb771866d639b9e49a66cdb8e32fa42e15f07bbd06bd78e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cf4de1694de02a5c291b124d1ee62020b8ab8997a1e951977e6450a526d00a236918af520e1e0c4e63036c50731a07932d734b8a1f4b825f17661cb30f23e0f
|
7
|
+
data.tar.gz: 0c157b4af9bdd4a3ade74ccc12a4f2ea79162d45395d28e34c30e2d7cb7535b9137d37ed0688852b0b52834cf38c7a4207b11797deaa201d08d227c4a6e714ce
|
data/lib/async/debug/monitor.rb
CHANGED
@@ -20,25 +20,23 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
+
require 'delegate'
|
24
|
+
|
23
25
|
module Async
|
24
26
|
module Debug
|
25
|
-
class Monitor
|
27
|
+
class Monitor < Delegator
|
26
28
|
def initialize(monitor, selector)
|
27
29
|
@monitor = monitor
|
28
30
|
@selector = selector
|
29
31
|
end
|
30
32
|
|
31
|
-
def
|
32
|
-
@
|
33
|
-
@monitor.close
|
33
|
+
def __getobj__
|
34
|
+
@monitor
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
@
|
38
|
-
|
39
|
-
|
40
|
-
def respond_to?(*arguments)
|
41
|
-
@monitor.respond_to?(*arguments)
|
37
|
+
def close
|
38
|
+
@selector.deregister(self)
|
39
|
+
@monitor.close
|
42
40
|
end
|
43
41
|
|
44
42
|
def inspect
|
data/lib/async/debug/selector.rb
CHANGED
@@ -24,6 +24,7 @@ require_relative 'monitor'
|
|
24
24
|
require_relative '../logger'
|
25
25
|
|
26
26
|
require 'nio'
|
27
|
+
require 'set'
|
27
28
|
|
28
29
|
module Async
|
29
30
|
module Debug
|
@@ -36,7 +37,7 @@ module Async
|
|
36
37
|
class Selector
|
37
38
|
def initialize(selector = NIO::Selector.new)
|
38
39
|
@selector = selector
|
39
|
-
@monitors =
|
40
|
+
@monitors = Set.new
|
40
41
|
end
|
41
42
|
|
42
43
|
def register(object, interests)
|
@@ -46,26 +47,18 @@ module Async
|
|
46
47
|
raise RuntimeError, "Could not convert #{io} into IO!"
|
47
48
|
end
|
48
49
|
|
49
|
-
if monitor = @monitors[io.fileno]
|
50
|
-
raise RuntimeError, "Trying to register monitor for #{object.inspect} but it was already registered: #{monitor.inspect}!"
|
51
|
-
end
|
52
|
-
|
53
50
|
monitor = Monitor.new(@selector.register(object, interests), self)
|
54
51
|
|
55
|
-
@monitors
|
52
|
+
@monitors.add(monitor)
|
56
53
|
|
57
54
|
return monitor
|
58
55
|
end
|
59
56
|
|
60
|
-
def deregister(
|
61
|
-
Async.logger.debug(self) {"Deregistering #{
|
62
|
-
|
63
|
-
unless io = ::IO.try_convert(object)
|
64
|
-
raise RuntimeError, "Could not convert #{io} into IO!"
|
65
|
-
end
|
57
|
+
def deregister(monitor)
|
58
|
+
Async.logger.debug(self) {"Deregistering #{monitor.inspect}."}
|
66
59
|
|
67
|
-
unless @monitors.delete(
|
68
|
-
raise RuntimeError, "Trying to remove monitor for #{
|
60
|
+
unless @monitors.delete?(monitor)
|
61
|
+
raise RuntimeError, "Trying to remove monitor for #{monitor.inspect} but it was not registered!"
|
69
62
|
end
|
70
63
|
end
|
71
64
|
|
@@ -75,7 +68,7 @@ module Async
|
|
75
68
|
|
76
69
|
def close
|
77
70
|
if @monitors.any?
|
78
|
-
raise LeakError, @monitors
|
71
|
+
raise LeakError, @monitors
|
79
72
|
end
|
80
73
|
ensure
|
81
74
|
@selector.close
|
data/lib/async/node.rb
CHANGED
@@ -24,6 +24,7 @@ module Async
|
|
24
24
|
# A double linked list.
|
25
25
|
class List
|
26
26
|
def initialize
|
27
|
+
# The list behaves like a list node, so @tail points to the next item (the first one) and head points to the previous item (the last one). This may be slightly confusing but it makes the interface more natural.
|
27
28
|
@head = nil
|
28
29
|
@tail = nil
|
29
30
|
@size = 0
|
@@ -36,21 +37,21 @@ module Async
|
|
36
37
|
|
37
38
|
# Inserts an item at the end of the list.
|
38
39
|
def insert(item)
|
39
|
-
unless @
|
40
|
-
@head = item
|
40
|
+
unless @tail
|
41
41
|
@tail = item
|
42
|
+
@head = item
|
42
43
|
|
43
44
|
# Consistency:
|
44
45
|
item.head = nil
|
45
46
|
item.tail = nil
|
46
47
|
else
|
47
|
-
@
|
48
|
-
item.head = @
|
48
|
+
@head.tail = item
|
49
|
+
item.head = @head
|
49
50
|
|
50
51
|
# Consistency:
|
51
52
|
item.tail = nil
|
52
53
|
|
53
|
-
@
|
54
|
+
@head = item
|
54
55
|
end
|
55
56
|
|
56
57
|
@size += 1
|
@@ -59,14 +60,14 @@ module Async
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def delete(item)
|
62
|
-
if @
|
63
|
-
@
|
63
|
+
if @tail.equal?(item)
|
64
|
+
@tail = @tail.tail
|
64
65
|
else
|
65
66
|
item.head.tail = item.tail
|
66
67
|
end
|
67
68
|
|
68
|
-
if @
|
69
|
-
@
|
69
|
+
if @head.equal?(item)
|
70
|
+
@head = @head.head
|
70
71
|
else
|
71
72
|
item.tail.head = item.head
|
72
73
|
end
|
@@ -79,15 +80,17 @@ module Async
|
|
79
80
|
return self
|
80
81
|
end
|
81
82
|
|
82
|
-
def each
|
83
|
+
def each(&block)
|
83
84
|
return to_enum unless block_given?
|
84
85
|
|
85
|
-
|
86
|
-
while
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
86
|
+
current = self
|
87
|
+
while node = current.tail
|
88
|
+
yield node
|
89
|
+
|
90
|
+
# If the node has deleted itself or any subsequent node, it will no longer be the next node, so don't use it for continued traversal:
|
91
|
+
if current.tail.equal?(node)
|
92
|
+
current = node
|
93
|
+
end
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
@@ -100,19 +103,19 @@ module Async
|
|
100
103
|
end
|
101
104
|
|
102
105
|
def first
|
103
|
-
@
|
106
|
+
@tail
|
104
107
|
end
|
105
108
|
|
106
109
|
def last
|
107
|
-
@
|
110
|
+
@head
|
108
111
|
end
|
109
112
|
|
110
113
|
def empty?
|
111
|
-
@
|
114
|
+
@tail.nil?
|
112
115
|
end
|
113
116
|
|
114
117
|
def nil?
|
115
|
-
@
|
118
|
+
@tail.nil?
|
116
119
|
end
|
117
120
|
end
|
118
121
|
|
@@ -276,6 +279,8 @@ module Async
|
|
276
279
|
if child.finished?
|
277
280
|
delete_child(child)
|
278
281
|
else
|
282
|
+
# In theory we don't need to do this... because we are throwing away the list. However, if you don't correctly update the list when moving the child to the parent, it foobars the enumeration, and subsequent nodes will be skipped, or in the worst case you might start enumerating the parents nodes.
|
283
|
+
delete_child(child)
|
279
284
|
parent.add_child(child)
|
280
285
|
end
|
281
286
|
end
|
@@ -297,8 +302,31 @@ module Async
|
|
297
302
|
end
|
298
303
|
end
|
299
304
|
|
300
|
-
|
301
|
-
|
305
|
+
# Immediately terminate all children tasks, including transient tasks.
|
306
|
+
# Internally invokes `stop(false)` on all children.
|
307
|
+
def terminate
|
308
|
+
# Attempt to stop the current task immediately, and all children:
|
309
|
+
stop(false)
|
310
|
+
|
311
|
+
# If that doesn't work, take more serious action:
|
312
|
+
@children&.each do |child|
|
313
|
+
child.terminate
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Attempt to stop the current node immediately, including all non-transient children.
|
318
|
+
# Invokes {#stop_children} to stop all children.
|
319
|
+
# @parameter later [Boolean] Whether to defer stopping until some point in the future.
|
320
|
+
def stop(later = false)
|
321
|
+
# The implementation of this method may defer calling `stop_children`.
|
322
|
+
stop_children(later)
|
323
|
+
end
|
324
|
+
|
325
|
+
# Attempt to stop all non-transient children.
|
326
|
+
private def stop_children(later = false)
|
327
|
+
@children&.each do |child|
|
328
|
+
child.stop(later) unless child.transient?
|
329
|
+
end
|
302
330
|
end
|
303
331
|
|
304
332
|
def print_hierarchy(out = $stdout, backtrace: true)
|
data/lib/async/reactor.rb
CHANGED
@@ -47,9 +47,7 @@ module Async
|
|
47
47
|
# running.
|
48
48
|
def self.run(*arguments, **options, &block)
|
49
49
|
if current = Task.current?
|
50
|
-
|
51
|
-
|
52
|
-
return reactor.async(*arguments, **options, &block)
|
50
|
+
return current.async(*arguments, **options, &block)
|
53
51
|
else
|
54
52
|
reactor = self.new
|
55
53
|
|
@@ -96,6 +94,7 @@ module Async
|
|
96
94
|
end
|
97
95
|
|
98
96
|
attr :scheduler
|
97
|
+
attr :logger
|
99
98
|
|
100
99
|
# @reentrant Not thread safe.
|
101
100
|
def block(blocker, timeout)
|
@@ -111,7 +110,7 @@ module Async
|
|
111
110
|
|
112
111
|
begin
|
113
112
|
@blocked += 1
|
114
|
-
|
113
|
+
Task.yield
|
115
114
|
ensure
|
116
115
|
@blocked -= 1
|
117
116
|
end
|
@@ -135,10 +134,6 @@ module Async
|
|
135
134
|
end
|
136
135
|
end
|
137
136
|
|
138
|
-
def logger
|
139
|
-
@logger || Console.logger
|
140
|
-
end
|
141
|
-
|
142
137
|
def to_s
|
143
138
|
"\#<#{self.description} #{@children&.size || 0} children (#{stopped? ? 'stopped' : 'running'})>"
|
144
139
|
end
|
@@ -167,7 +162,7 @@ module Async
|
|
167
162
|
# - Avoid scheduler overhead if no blocking operation is performed.
|
168
163
|
task.run(*arguments)
|
169
164
|
|
170
|
-
# logger.debug "Initial execution of task #{fiber} complete (#{result} -> #{fiber.alive?})..."
|
165
|
+
# Console.logger.debug "Initial execution of task #{fiber} complete (#{result} -> #{fiber.alive?})..."
|
171
166
|
return task
|
172
167
|
end
|
173
168
|
|
@@ -198,7 +193,7 @@ module Async
|
|
198
193
|
def yield(fiber = Fiber.current)
|
199
194
|
@ready << fiber
|
200
195
|
|
201
|
-
|
196
|
+
Task.yield
|
202
197
|
end
|
203
198
|
|
204
199
|
def finished?
|
@@ -210,7 +205,7 @@ module Async
|
|
210
205
|
# @param timeout [Float | nil] the maximum timeout, or if nil, indefinite.
|
211
206
|
# @return [Boolean] whether there is more work to do.
|
212
207
|
def run_once(timeout = nil)
|
213
|
-
# logger.debug(self) {"@ready = #{@ready} @running = #{@running}"}
|
208
|
+
# Console.logger.debug(self) {"@ready = #{@ready} @running = #{@running}"}
|
214
209
|
|
215
210
|
if @ready.any?
|
216
211
|
# running used to correctly answer on `finished?`, and to reuse Array object.
|
@@ -258,7 +253,7 @@ module Async
|
|
258
253
|
interval = timeout
|
259
254
|
end
|
260
255
|
|
261
|
-
# logger.info(self) {"Selecting with #{@children&.size} children with interval = #{interval ? interval.round(2) : 'infinite'}..."}
|
256
|
+
# Console.logger.info(self) {"Selecting with #{@children&.size} children with interval = #{interval ? interval.round(2) : 'infinite'}..."}
|
262
257
|
if monitors = @selector.select(interval)
|
263
258
|
monitors.each do |monitor|
|
264
259
|
monitor.value.resume
|
@@ -295,35 +290,26 @@ module Async
|
|
295
290
|
return initial_task
|
296
291
|
ensure
|
297
292
|
@scheduler&.clear!
|
298
|
-
logger.debug(self) {"Exiting run-loop because #{$! ? $! : 'finished'}."}
|
299
|
-
end
|
300
|
-
|
301
|
-
def stop(later = true)
|
302
|
-
@children&.each do |child|
|
303
|
-
# We don't want this process to propagate `Async::Stop` exceptions, so we schedule tasks to stop later.
|
304
|
-
child.stop(later)
|
305
|
-
end
|
293
|
+
Console.logger.debug(self) {"Exiting run-loop because #{$! ? $! : 'finished'}."}
|
306
294
|
end
|
307
295
|
|
308
296
|
# Stop each of the children tasks and close the selector.
|
309
|
-
#
|
310
|
-
# @return [void]
|
311
297
|
def close
|
312
|
-
# This is a critical step. Because tasks could be stored as instance variables, and since the reactor is (probably) going out of scope, we need to ensure they are stopped. Otherwise, the tasks will belong to a reactor that will never run again and are not stopped
|
313
|
-
self.
|
298
|
+
# This is a critical step. Because tasks could be stored as instance variables, and since the reactor is (probably) going out of scope, we need to ensure they are stopped. Otherwise, the tasks will belong to a reactor that will never run again and are not stopped:
|
299
|
+
self.terminate
|
314
300
|
|
315
301
|
@selector.close
|
316
302
|
@selector = nil
|
317
303
|
end
|
318
304
|
|
319
305
|
# Check if the selector has been closed.
|
320
|
-
# @
|
306
|
+
# @returns [Boolean]
|
321
307
|
def closed?
|
322
308
|
@selector.nil?
|
323
309
|
end
|
324
310
|
|
325
311
|
# Put the calling fiber to sleep for a given ammount of time.
|
326
|
-
# @
|
312
|
+
# @parameter duration [Numeric] The time in seconds, to sleep for.
|
327
313
|
def sleep(duration)
|
328
314
|
fiber = Fiber.current
|
329
315
|
|
@@ -347,7 +333,7 @@ module Async
|
|
347
333
|
timer = @timers.after(timeout) do
|
348
334
|
if fiber.alive?
|
349
335
|
error = exception.new("execution expired")
|
350
|
-
fiber.resume
|
336
|
+
fiber.resume(error)
|
351
337
|
end
|
352
338
|
end
|
353
339
|
|
data/lib/async/scheduler.rb
CHANGED
data/lib/async/task.rb
CHANGED
@@ -81,11 +81,13 @@ module Async
|
|
81
81
|
@result = nil
|
82
82
|
@finished = finished
|
83
83
|
|
84
|
-
@logger = logger
|
84
|
+
@logger = logger || @parent.logger
|
85
85
|
|
86
86
|
@fiber = make_fiber(&block)
|
87
87
|
end
|
88
88
|
|
89
|
+
attr :logger
|
90
|
+
|
89
91
|
if Fiber.current.respond_to?(:backtrace)
|
90
92
|
def backtrace(*arguments)
|
91
93
|
@fiber&.backtrace(*arguments)
|
@@ -96,10 +98,6 @@ module Async
|
|
96
98
|
"\#<#{self.description} (#{@status})>"
|
97
99
|
end
|
98
100
|
|
99
|
-
def logger
|
100
|
-
@logger || Console.logger
|
101
|
-
end
|
102
|
-
|
103
101
|
# @attr ios [Reactor] The reactor the task was created within.
|
104
102
|
attr :reactor
|
105
103
|
|
@@ -158,7 +156,6 @@ module Async
|
|
158
156
|
# Soon to become attr :result
|
159
157
|
|
160
158
|
# Stop the task and all of its children.
|
161
|
-
# @return [void]
|
162
159
|
def stop(later = false)
|
163
160
|
if self.stopped?
|
164
161
|
# If we already stopped this task... don't try to stop it again:
|
@@ -242,9 +239,9 @@ module Async
|
|
242
239
|
raise
|
243
240
|
elsif @finished.nil?
|
244
241
|
# If no one has called wait, we log this as an error:
|
245
|
-
logger.error(self) {$!}
|
242
|
+
Console.logger.error(self) {$!}
|
246
243
|
else
|
247
|
-
logger.debug(self) {$!}
|
244
|
+
Console.logger.debug(self) {$!}
|
248
245
|
end
|
249
246
|
end
|
250
247
|
|
@@ -252,9 +249,7 @@ module Async
|
|
252
249
|
# logger.debug(self) {"Task was stopped with #{@children&.size.inspect} children!"}
|
253
250
|
@status = :stopped
|
254
251
|
|
255
|
-
|
256
|
-
child.stop(true)
|
257
|
-
end
|
252
|
+
stop_children(true)
|
258
253
|
end
|
259
254
|
|
260
255
|
def make_fiber(&block)
|
@@ -264,7 +259,7 @@ module Async
|
|
264
259
|
begin
|
265
260
|
@result = yield(self, *arguments)
|
266
261
|
@status = :complete
|
267
|
-
# logger.debug(self) {"Task was completed with #{@children.size} children!"}
|
262
|
+
# Console.logger.debug(self) {"Task was completed with #{@children.size} children!"}
|
268
263
|
rescue Stop
|
269
264
|
stop!
|
270
265
|
rescue StandardError => error
|
@@ -272,7 +267,7 @@ module Async
|
|
272
267
|
rescue Exception => exception
|
273
268
|
fail!(exception, true)
|
274
269
|
ensure
|
275
|
-
# logger.debug(self) {"Task ensure $!=#{$!} with #{@children.size} children!"}
|
270
|
+
# Console.logger.debug(self) {"Task ensure $!=#{$!} with #{@children.size} children!"}
|
276
271
|
finish!
|
277
272
|
end
|
278
273
|
end
|
data/lib/async/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.29.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: console
|
@@ -179,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
181
|
requirements: []
|
182
|
-
rubygems_version: 3.2.
|
182
|
+
rubygems_version: 3.2.15
|
183
183
|
signing_key:
|
184
184
|
specification_version: 4
|
185
185
|
summary: A concurrency framework for Ruby.
|