async 1.6.0 → 1.7.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.
- checksums.yaml +4 -4
- data/async.gemspec +1 -1
- data/lib/async/debug.rb +86 -0
- data/lib/async/reactor.rb +15 -4
- data/lib/async/version.rb +1 -1
- data/lib/async/wrapper.rb +9 -5
- data/spec/async/performance_spec.rb +2 -0
- data/spec/async/wrapper_spec.rb +18 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f19a934a9510bb56c88e54ac305707a0cd68d5a74f925e56a2f6ca69c5a0bab9
|
4
|
+
data.tar.gz: 8c68511531f459a2c067550f369e9253d7c93064f054a08780a11f357d68a942
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1387cc04e3d358d9b39dc5f2fb59e457b173f04b3381c603a11d495f987f29c41753c6addae55fcb203e6e1a3f96bf929e45cafddf624fd374fc85b6bc8e33f
|
7
|
+
data.tar.gz: e76e0e5dc3c67f6d77209f501a58ca1c97dbc8f96d40081ec55d082e0f0be63e7ecd344ba4adfcbaadcc910590d1abdf4a4eab0bd4a0ab8310f5afbe5a55db7d
|
data/async.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.required_ruby_version = ">= 2.2.7"
|
25
25
|
|
26
|
-
spec.add_runtime_dependency "nio4r", "~> 2.
|
26
|
+
spec.add_runtime_dependency "nio4r", "~> 2.3"
|
27
27
|
spec.add_runtime_dependency "timers", "~> 4.1"
|
28
28
|
|
29
29
|
spec.add_development_dependency "async-rspec", "~> 1.1"
|
data/lib/async/debug.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'nio'
|
22
|
+
|
23
|
+
module Async
|
24
|
+
class DebugSelector
|
25
|
+
class MonitorProxy
|
26
|
+
def initialize(monitor, selector)
|
27
|
+
@monitor = monitor
|
28
|
+
@selector = selector
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
@selector.deregister(@monitor.io)
|
33
|
+
@monitor.close
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(*args, &block)
|
37
|
+
@monitor.send(*args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def respond_to?(*args)
|
41
|
+
@monitor.respond_to?(*args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(selector)
|
46
|
+
@selector = selector
|
47
|
+
@monitors = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def register(io, interests)
|
51
|
+
$stderr.puts "Registering #{io.inspect} for #{interests}."
|
52
|
+
|
53
|
+
if monitor = @monitors[io.fileno]
|
54
|
+
raise RuntimeError, "Trying to register monitor for #{io.inspect} but it was already registered as #{monitor.io.inspect}!"
|
55
|
+
end
|
56
|
+
|
57
|
+
@monitors[io.fileno] = io
|
58
|
+
|
59
|
+
MonitorProxy.new(@selector.register(io, interests), self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def deregister(io)
|
63
|
+
$stderr.puts "Deregistering #{io.inspect}."
|
64
|
+
|
65
|
+
unless @monitors.delete(io.fileno)
|
66
|
+
raise RuntimeError, "Trying to remove monitor for #{io.inspect} but it was not registered!"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def wakeup
|
71
|
+
@selector.wakeup
|
72
|
+
end
|
73
|
+
|
74
|
+
def close
|
75
|
+
if @monitors.any?
|
76
|
+
$stderr.puts "Trying to close selector with active monitors: #{@monitors.values.inspect}!"
|
77
|
+
end
|
78
|
+
|
79
|
+
@selector.close
|
80
|
+
end
|
81
|
+
|
82
|
+
def select(*args)
|
83
|
+
@selector.select(*args)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/async/reactor.rb
CHANGED
@@ -30,7 +30,17 @@ module Async
|
|
30
30
|
# Raised if a timeout occurs on a specific Fiber. Handled gracefully by {Task}.
|
31
31
|
class TimeoutError < RuntimeError
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
|
+
class MonitorError < RuntimeError
|
35
|
+
def initialize(monitor)
|
36
|
+
super "Event detected on IO #{monitor.io.inspect} (#{monitor.interests} -> #{monitor.readiness}) without corresponding fiber!"
|
37
|
+
|
38
|
+
@monitor = monitor
|
39
|
+
end
|
40
|
+
|
41
|
+
attr :monitor
|
42
|
+
end
|
43
|
+
|
34
44
|
# An asynchronous, cooperatively scheduled event reactor.
|
35
45
|
class Reactor < Node
|
36
46
|
extend Forwardable
|
@@ -165,20 +175,21 @@ module Async
|
|
165
175
|
interval = 0
|
166
176
|
end
|
167
177
|
|
168
|
-
# Async.logger.debug{"
|
178
|
+
# Async.logger.debug(self) {"Updating #{@children.count} children..."}
|
169
179
|
# As timeouts may have been updated, and caused fibers to complete, we should check this.
|
170
180
|
|
171
181
|
# If there is nothing to do, then finish:
|
172
|
-
# Async.logger.debug{"[#{self}] @children.empty? = #{@children.empty?} && interval #{interval.inspect}"}
|
173
182
|
if !interval && self.finished?
|
174
183
|
return initial_task
|
175
184
|
end
|
176
185
|
|
177
|
-
# Async.logger.debug{"Selecting with #{@children.count} fibers interval = #{interval.inspect}..."}
|
186
|
+
# Async.logger.debug(self) {"Selecting with #{@children.count} fibers interval = #{interval.inspect}..."}
|
178
187
|
if monitors = @selector.select(interval)
|
179
188
|
monitors.each do |monitor|
|
180
189
|
if fiber = monitor.value
|
181
190
|
fiber.resume # if fiber.alive?
|
191
|
+
else
|
192
|
+
raise MonitorError.new(monitor)
|
182
193
|
end
|
183
194
|
end
|
184
195
|
end
|
data/lib/async/version.rb
CHANGED
data/lib/async/wrapper.rb
CHANGED
@@ -42,10 +42,12 @@ module Async
|
|
42
42
|
# Bind this wrapper to a different reactor. Assign nil to convert to an unbound wrapper (can be used from any reactor/task but with slightly increased overhead.)
|
43
43
|
# Binding to a reactor is purely a performance consideration. Generally, I don't like APIs that exist only due to optimisations. This is borderline, so consider this functionality semi-private.
|
44
44
|
def reactor= reactor
|
45
|
-
|
45
|
+
if @monitor
|
46
|
+
@monitor.close
|
47
|
+
@monitor = nil
|
48
|
+
end
|
46
49
|
|
47
50
|
@reactor = reactor
|
48
|
-
@monitor = nil
|
49
51
|
end
|
50
52
|
|
51
53
|
# Wait for the io to become readable.
|
@@ -74,8 +76,7 @@ module Async
|
|
74
76
|
begin
|
75
77
|
wait_for(@reactor, @monitor, duration)
|
76
78
|
ensure
|
77
|
-
@monitor.
|
78
|
-
@monitor.value = nil
|
79
|
+
@monitor.interests = nil
|
79
80
|
end
|
80
81
|
else
|
81
82
|
reactor = Task.current.reactor
|
@@ -91,7 +92,10 @@ module Async
|
|
91
92
|
|
92
93
|
# Close the io and monitor.
|
93
94
|
def close
|
94
|
-
|
95
|
+
if @monitor
|
96
|
+
@monitor.close
|
97
|
+
@monitor = nil
|
98
|
+
end
|
95
99
|
|
96
100
|
@io.close
|
97
101
|
end
|
data/spec/async/wrapper_spec.rb
CHANGED
@@ -46,6 +46,24 @@ RSpec.describe Async::Wrapper do
|
|
46
46
|
input.close
|
47
47
|
output.close
|
48
48
|
end
|
49
|
+
|
50
|
+
it "can wait for readability in multiple tasks" do
|
51
|
+
reactor.async do
|
52
|
+
input.wait_writable(1)
|
53
|
+
input.io.write('Hello World')
|
54
|
+
end
|
55
|
+
|
56
|
+
output.reactor = reactor
|
57
|
+
|
58
|
+
2.times do
|
59
|
+
reactor.async do
|
60
|
+
expect(output.wait_readable(1)).to be_truthy
|
61
|
+
end.wait
|
62
|
+
end
|
63
|
+
|
64
|
+
input.close
|
65
|
+
output.close
|
66
|
+
end
|
49
67
|
end
|
50
68
|
|
51
69
|
describe '#reactor=' do
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: timers
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- lib/async.rb
|
121
121
|
- lib/async/clock.rb
|
122
122
|
- lib/async/condition.rb
|
123
|
+
- lib/async/debug.rb
|
123
124
|
- lib/async/logger.rb
|
124
125
|
- lib/async/measure.rb
|
125
126
|
- lib/async/node.rb
|