async 1.7.0 → 1.8.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/Rakefile +1 -0
- data/lib/async/{debug.rb → debug/monitor.rb} +4 -43
- data/lib/async/debug/selector.rb +81 -0
- data/lib/async/reactor.rb +11 -22
- data/lib/async/version.rb +1 -1
- data/lib/async/wrapper.rb +135 -39
- data/spec/async/logger_spec.rb +23 -21
- data/spec/async/reactor_spec.rb +47 -25
- data/spec/async/wrapper_spec.rb +109 -21
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8342091acbc6da153552ca269239949b27898eb7bff55ca9425687e86f314751
|
4
|
+
data.tar.gz: fa8fd142667ba572fd66554d25bf9490625811f91005c9c4b0ac76f9329dba69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8e9d025de96cee15d9967e344a81dd01e3c3f6e64895f1deba1f2fd77edbbff2d9d7d63294cdaca6492add2112e5b0742e326c70845c52b8041b10eba96fd17
|
7
|
+
data.tar.gz: 4f07e3153419bd128c53c86f63fca88dcde579d906689e9bbaff488b6f782f11d3950a1f6754894f20880aa8aa09fcde50b7608c0c47b97ef0fe9314b5d5343b
|
data/Rakefile
CHANGED
@@ -18,11 +18,9 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
require 'nio'
|
22
|
-
|
23
21
|
module Async
|
24
|
-
|
25
|
-
class
|
22
|
+
module Debug
|
23
|
+
class Monitor
|
26
24
|
def initialize(monitor, selector)
|
27
25
|
@monitor = monitor
|
28
26
|
@selector = selector
|
@@ -40,47 +38,10 @@ module Async
|
|
40
38
|
def respond_to?(*args)
|
41
39
|
@monitor.respond_to?(*args)
|
42
40
|
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
41
|
|
53
|
-
|
54
|
-
|
42
|
+
def inspect
|
43
|
+
"\#<#{self.class} io=#{@monitor.io.inspect} interests=#{@monitor.interests.inspect} readiness=#{@monitor.readiness.inspect}>"
|
55
44
|
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
45
|
end
|
85
46
|
end
|
86
47
|
end
|
@@ -0,0 +1,81 @@
|
|
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_relative 'monitor'
|
22
|
+
require_relative '../logger'
|
23
|
+
|
24
|
+
require 'nio'
|
25
|
+
|
26
|
+
module Async
|
27
|
+
module Debug
|
28
|
+
class Selector
|
29
|
+
def initialize(selector = NIO::Selector.new)
|
30
|
+
@selector = selector
|
31
|
+
@monitors = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def register(object, interests)
|
35
|
+
Async.logger.debug(self) {"Registering #{object.inspect} for #{interests}."}
|
36
|
+
|
37
|
+
unless io = ::IO.try_convert(object)
|
38
|
+
raise RuntimeError, "Could not convert #{io} into IO!"
|
39
|
+
end
|
40
|
+
|
41
|
+
if monitor = @monitors[io.fileno]
|
42
|
+
raise RuntimeError, "Trying to register monitor for #{object.inspect} but it was already registered: #{monitor.inspect}!"
|
43
|
+
end
|
44
|
+
|
45
|
+
monitor = Monitor.new(@selector.register(object, interests), self)
|
46
|
+
|
47
|
+
@monitors[io.fileno] = monitor
|
48
|
+
|
49
|
+
return monitor
|
50
|
+
end
|
51
|
+
|
52
|
+
def deregister(object)
|
53
|
+
Async.logger.debug(self) {"Deregistering #{object.inspect}."}
|
54
|
+
|
55
|
+
unless io = ::IO.try_convert(object)
|
56
|
+
raise RuntimeError, "Could not convert #{io} into IO!"
|
57
|
+
end
|
58
|
+
|
59
|
+
unless @monitors.delete(io.fileno)
|
60
|
+
raise RuntimeError, "Trying to remove monitor for #{io.inspect} but it was not registered!"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def wakeup
|
65
|
+
@selector.wakeup
|
66
|
+
end
|
67
|
+
|
68
|
+
def close
|
69
|
+
if @monitors.any?
|
70
|
+
raise RuntimeError, "Trying to close selector with active monitors: #{@monitors.values.inspect}!"
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
@selector.close
|
74
|
+
end
|
75
|
+
|
76
|
+
def select(*args)
|
77
|
+
@selector.select(*args)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/async/reactor.rb
CHANGED
@@ -31,16 +31,6 @@ module Async
|
|
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
|
-
|
44
34
|
# An asynchronous, cooperatively scheduled event reactor.
|
45
35
|
class Reactor < Node
|
46
36
|
extend Forwardable
|
@@ -68,10 +58,10 @@ module Async
|
|
68
58
|
end
|
69
59
|
end
|
70
60
|
|
71
|
-
def initialize
|
72
|
-
super
|
61
|
+
def initialize(parent = nil, selector: NIO::Selector.new)
|
62
|
+
super(parent)
|
73
63
|
|
74
|
-
@selector =
|
64
|
+
@selector = selector
|
75
65
|
@timers = Timers::Group.new
|
76
66
|
|
77
67
|
@ready = []
|
@@ -86,6 +76,10 @@ module Async
|
|
86
76
|
# @attr stopped [Boolean]
|
87
77
|
attr :stopped
|
88
78
|
|
79
|
+
def stopped?
|
80
|
+
@stopped
|
81
|
+
end
|
82
|
+
|
89
83
|
def_delegators :@timers, :every, :after
|
90
84
|
|
91
85
|
# Start an asynchronous task within the specified reactor. The task will be
|
@@ -112,10 +106,9 @@ module Async
|
|
112
106
|
return task
|
113
107
|
end
|
114
108
|
|
115
|
-
def register(
|
116
|
-
monitor = @selector.register(
|
117
|
-
|
118
|
-
monitor.value = Fiber.current
|
109
|
+
def register(io, interest, value = Fiber.current)
|
110
|
+
monitor = @selector.register(io, interest)
|
111
|
+
monitor.value = value
|
119
112
|
|
120
113
|
return monitor
|
121
114
|
end
|
@@ -186,11 +179,7 @@ module Async
|
|
186
179
|
# Async.logger.debug(self) {"Selecting with #{@children.count} fibers interval = #{interval.inspect}..."}
|
187
180
|
if monitors = @selector.select(interval)
|
188
181
|
monitors.each do |monitor|
|
189
|
-
|
190
|
-
fiber.resume # if fiber.alive?
|
191
|
-
else
|
192
|
-
raise MonitorError.new(monitor)
|
193
|
-
end
|
182
|
+
monitor.value.resume
|
194
183
|
end
|
195
184
|
end
|
196
185
|
end until @stopped
|
data/lib/async/version.rb
CHANGED
data/lib/async/wrapper.rb
CHANGED
@@ -23,14 +23,46 @@ require 'nio'
|
|
23
23
|
module Async
|
24
24
|
# Represents an asynchronous IO within a reactor.
|
25
25
|
class Wrapper
|
26
|
+
class Cancelled < StandardError
|
27
|
+
def initialize
|
28
|
+
super "The operation has been cancelled!"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# wait_readable, wait_writable and wait_any are not re-entrant, and will raise this failure.
|
33
|
+
class WaitError < StandardError
|
34
|
+
def initialize
|
35
|
+
super "A fiber is already waiting!"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
26
39
|
# @param io the native object to wrap.
|
27
40
|
# @param reactor [Reactor] the reactor that is managing this wrapper, or not specified, it's looked up by way of {Task.current}.
|
28
|
-
# @param bound [Boolean] whether the underlying socket will be closed if the wrapper is closed.
|
29
41
|
def initialize(io, reactor = nil)
|
30
42
|
@io = io
|
31
43
|
|
32
44
|
@reactor = reactor
|
33
45
|
@monitor = nil
|
46
|
+
|
47
|
+
@readable = nil
|
48
|
+
@writable = nil
|
49
|
+
@any = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def resume(*args)
|
53
|
+
readiness = @monitor.readiness
|
54
|
+
|
55
|
+
if @readable and (readiness == :r or readiness == :rw)
|
56
|
+
@readable.resume(*args)
|
57
|
+
end
|
58
|
+
|
59
|
+
if @writable and (readiness == :w or readiness == :rw)
|
60
|
+
@writable.resume(*args)
|
61
|
+
end
|
62
|
+
|
63
|
+
if @any
|
64
|
+
@any.resume(*args)
|
65
|
+
end
|
34
66
|
end
|
35
67
|
|
36
68
|
# The underlying native `io`.
|
@@ -39,74 +71,138 @@ module Async
|
|
39
71
|
# The reactor this wrapper is associated with, if any.
|
40
72
|
attr :reactor
|
41
73
|
|
74
|
+
# The monitor for this wrapper, if any.
|
75
|
+
attr :monitor
|
76
|
+
|
42
77
|
# 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
78
|
# 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
79
|
def reactor= reactor
|
45
|
-
if @
|
46
|
-
|
47
|
-
|
48
|
-
end
|
80
|
+
return if @reactor.equal?(reactor)
|
81
|
+
|
82
|
+
cancel_monitor
|
49
83
|
|
50
84
|
@reactor = reactor
|
51
85
|
end
|
52
86
|
|
53
87
|
# Wait for the io to become readable.
|
54
88
|
def wait_readable(duration = nil)
|
55
|
-
|
89
|
+
raise WaitError if @readable
|
90
|
+
|
91
|
+
self.reactor = Task.current.reactor
|
92
|
+
|
93
|
+
begin
|
94
|
+
@readable = Fiber.current
|
95
|
+
wait_for(duration)
|
96
|
+
ensure
|
97
|
+
@readable = nil
|
98
|
+
@monitor.interests = interests if @monitor
|
99
|
+
end
|
56
100
|
end
|
57
101
|
|
58
102
|
# Wait for the io to become writable.
|
59
103
|
def wait_writable(duration = nil)
|
60
|
-
|
104
|
+
raise WaitError if @writable
|
105
|
+
|
106
|
+
self.reactor = Task.current.reactor
|
107
|
+
|
108
|
+
begin
|
109
|
+
@writable = Fiber.current
|
110
|
+
wait_for(duration)
|
111
|
+
ensure
|
112
|
+
@writable = nil
|
113
|
+
@monitor.interests = interests if @monitor
|
114
|
+
end
|
61
115
|
end
|
62
116
|
|
63
117
|
# Wait fo the io to become either readable or writable.
|
64
|
-
# @param interests [:r | :w | :rw] what events to wait for.
|
65
118
|
# @param duration [Float] timeout after the given duration if not `nil`.
|
66
|
-
def wait_any(
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
wait_for(@reactor, @monitor, duration)
|
78
|
-
ensure
|
79
|
-
@monitor.interests = nil
|
80
|
-
end
|
81
|
-
else
|
82
|
-
reactor = Task.current.reactor
|
83
|
-
monitor = reactor.register(@io, interests)
|
84
|
-
|
85
|
-
begin
|
86
|
-
wait_for(reactor, monitor, duration)
|
87
|
-
ensure
|
88
|
-
monitor.close
|
89
|
-
end
|
119
|
+
def wait_any(duration = nil)
|
120
|
+
raise WaitError if @any
|
121
|
+
|
122
|
+
self.reactor = Task.current.reactor
|
123
|
+
|
124
|
+
begin
|
125
|
+
@any = Fiber.current
|
126
|
+
wait_for(duration)
|
127
|
+
ensure
|
128
|
+
@any = nil
|
129
|
+
@monitor.interests = interests if @monitor
|
90
130
|
end
|
91
131
|
end
|
92
132
|
|
93
133
|
# Close the io and monitor.
|
94
134
|
def close
|
95
|
-
|
96
|
-
@monitor.close
|
97
|
-
@monitor = nil
|
98
|
-
end
|
135
|
+
cancel_monitor
|
99
136
|
|
100
137
|
@io.close
|
101
138
|
end
|
102
139
|
|
140
|
+
def closed?
|
141
|
+
@io.closed?
|
142
|
+
end
|
143
|
+
|
103
144
|
private
|
104
145
|
|
105
|
-
|
146
|
+
# What an abomination.
|
147
|
+
def interests
|
148
|
+
if @any
|
149
|
+
return :rw
|
150
|
+
elsif @readable
|
151
|
+
if @writable
|
152
|
+
return :rw
|
153
|
+
else
|
154
|
+
return :r
|
155
|
+
end
|
156
|
+
elsif @writable
|
157
|
+
return :w
|
158
|
+
end
|
159
|
+
|
160
|
+
return nil
|
161
|
+
end
|
162
|
+
|
163
|
+
def cancel_monitor
|
164
|
+
if @readable
|
165
|
+
readable = @readable
|
166
|
+
@readable = nil
|
167
|
+
|
168
|
+
readable.resume(Cancelled.new)
|
169
|
+
end
|
170
|
+
|
171
|
+
if @writable
|
172
|
+
writable = @writable
|
173
|
+
@writable = nil
|
174
|
+
|
175
|
+
writable.resume(Cancelled.new)
|
176
|
+
end
|
177
|
+
|
178
|
+
if @any
|
179
|
+
any = @any
|
180
|
+
@any = nil
|
181
|
+
|
182
|
+
any.resume(Cancelled.new)
|
183
|
+
end
|
184
|
+
|
185
|
+
if @monitor
|
186
|
+
@monitor.close
|
187
|
+
@monitor = nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def wait_for(duration)
|
192
|
+
if @monitor
|
193
|
+
@monitor.interests = interests
|
194
|
+
else
|
195
|
+
@monitor = @reactor.register(@io, interests, self)
|
196
|
+
end
|
197
|
+
|
106
198
|
# If the user requested an explicit timeout for this operation:
|
107
199
|
if duration
|
108
|
-
reactor.timeout(duration) do
|
109
|
-
|
200
|
+
@reactor.timeout(duration) do
|
201
|
+
begin
|
202
|
+
Task.yield
|
203
|
+
rescue Async::TimeoutError
|
204
|
+
return false
|
205
|
+
end
|
110
206
|
end
|
111
207
|
else
|
112
208
|
Task.yield
|
data/spec/async/logger_spec.rb
CHANGED
@@ -19,29 +19,31 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
RSpec.describe Async.logger do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
let!(:verbose) {$VERBOSE}
|
26
|
-
after {$VERBOSE = verbose}
|
27
|
-
|
28
|
-
it 'should set default log level' do
|
29
|
-
$DEBUG = false
|
30
|
-
$VERBOSE = false
|
22
|
+
describe '::default_log_level' do
|
23
|
+
let!(:debug) {$DEBUG}
|
24
|
+
after {$DEBUG = debug}
|
31
25
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
it 'should set default log level based on $DEBUG' do
|
36
|
-
$DEBUG = true
|
26
|
+
let!(:verbose) {$VERBOSE}
|
27
|
+
after {$VERBOSE = verbose}
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
29
|
+
it 'should set default log level' do
|
30
|
+
$DEBUG = false
|
31
|
+
$VERBOSE = false
|
32
|
+
|
33
|
+
expect(Async.default_log_level).to be == Logger::WARN
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should set default log level based on $DEBUG' do
|
37
|
+
$DEBUG = true
|
38
|
+
|
39
|
+
expect(Async.default_log_level).to be == Logger::DEBUG
|
40
|
+
end
|
44
41
|
|
45
|
-
|
42
|
+
it 'should set default log level based on $VERBOSE' do
|
43
|
+
$DEBUG = false
|
44
|
+
$VERBOSE = true
|
45
|
+
|
46
|
+
expect(Async.default_log_level).to be == Logger::INFO
|
47
|
+
end
|
46
48
|
end
|
47
49
|
end
|
data/spec/async/reactor_spec.rb
CHANGED
@@ -19,38 +19,60 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
RSpec.describe Async::Reactor do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
task
|
28
|
-
|
22
|
+
describe '#run' do
|
23
|
+
it "can run tasks on different fibers" do
|
24
|
+
outer_fiber = Fiber.current
|
25
|
+
inner_fiber = nil
|
26
|
+
|
27
|
+
described_class.run do |task|
|
28
|
+
task.sleep(0)
|
29
|
+
inner_fiber = Fiber.current
|
30
|
+
end
|
31
|
+
|
32
|
+
expect(inner_fiber).to_not be nil
|
33
|
+
expect(outer_fiber).to_not be == inner_fiber
|
29
34
|
end
|
30
|
-
|
31
|
-
expect(inner_fiber).to_not be nil
|
32
|
-
expect(outer_fiber).to_not be == inner_fiber
|
33
35
|
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
describe '#stop' do
|
38
|
+
it "can be stop reactor" do
|
39
|
+
state = nil
|
40
|
+
|
41
|
+
subject.async do |task|
|
42
|
+
state = :started
|
43
|
+
task.sleep(10)
|
44
|
+
state = :stopped
|
45
|
+
end
|
46
|
+
|
47
|
+
subject.async do |task|
|
48
|
+
task.sleep(0.1)
|
49
|
+
task.reactor.stop
|
50
|
+
end
|
51
|
+
|
52
|
+
subject.run
|
53
|
+
|
54
|
+
expect(state).to be == :started
|
42
55
|
end
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
57
|
+
it "can stop reactor from different thread" do
|
58
|
+
events = Thread::Queue.new
|
59
|
+
|
60
|
+
thread = Thread.new do
|
61
|
+
if events.pop
|
62
|
+
subject.stop
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
subject.async do |task|
|
67
|
+
events << true
|
68
|
+
end
|
69
|
+
|
70
|
+
subject.run
|
71
|
+
|
72
|
+
thread.join
|
73
|
+
expect(subject).to be_stopped
|
47
74
|
end
|
48
|
-
|
49
|
-
subject.run
|
50
|
-
|
51
|
-
expect(state).to be == :started
|
52
75
|
end
|
53
|
-
|
54
76
|
it "can't return" do
|
55
77
|
expect do
|
56
78
|
Async::Reactor.run do |task|
|
data/spec/async/wrapper_spec.rb
CHANGED
@@ -21,61 +21,149 @@
|
|
21
21
|
require 'benchmark'
|
22
22
|
|
23
23
|
RSpec.describe Async::Wrapper do
|
24
|
+
include_context Async::RSpec::Reactor
|
25
|
+
|
24
26
|
let(:pipe) {IO.pipe}
|
25
27
|
let(:input) {Async::Wrapper.new(pipe.last)}
|
26
28
|
let(:output) {Async::Wrapper.new(pipe.first)}
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
+
after(:each) do
|
31
|
+
input.close unless input.closed?
|
32
|
+
output.close unless output.closed?
|
30
33
|
|
31
|
-
|
32
|
-
|
34
|
+
expect(input.monitor).to be_nil
|
35
|
+
expect(output.monitor).to be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#wait_readable' do
|
39
|
+
it "can wait to be readable" do
|
40
|
+
reader = reactor.async do
|
41
|
+
expect(output.wait_readable).to be_truthy
|
42
|
+
end
|
33
43
|
|
34
|
-
input.
|
35
|
-
|
44
|
+
input.io.write('Hello World')
|
45
|
+
reader.wait
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can timeout if no event occurs" do
|
49
|
+
expect(output.wait_readable(0.1)).to be_falsey
|
36
50
|
end
|
37
51
|
|
38
|
-
it "can wait for readability" do
|
52
|
+
it "can wait for readability in sequential tasks" do
|
39
53
|
reactor.async do
|
40
54
|
input.wait_writable(1)
|
41
55
|
input.io.write('Hello World')
|
42
56
|
end
|
43
57
|
|
44
|
-
|
58
|
+
2.times do
|
59
|
+
reactor.async do
|
60
|
+
expect(output.wait_readable(1)).to be_truthy
|
61
|
+
end.wait
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can be cancelled" do
|
66
|
+
reactor.async do
|
67
|
+
expect do
|
68
|
+
output.wait_readable
|
69
|
+
end.to raise_error(Async::Wrapper::Cancelled)
|
70
|
+
end
|
45
71
|
|
46
|
-
|
47
|
-
|
72
|
+
expect(output.monitor).to_not be_nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#wait_writable' do
|
77
|
+
it "can wait to be writable" do
|
78
|
+
expect(input.wait_writable).to be_truthy
|
48
79
|
end
|
49
80
|
|
50
|
-
it "can
|
81
|
+
it "can be cancelled" do
|
51
82
|
reactor.async do
|
52
|
-
|
83
|
+
expect do
|
84
|
+
input.wait_readable
|
85
|
+
end.to raise_error(Async::Wrapper::Cancelled)
|
86
|
+
end
|
87
|
+
|
88
|
+
expect(input.monitor).to_not be_nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#wait_any" do
|
93
|
+
it "can wait for any events" do
|
94
|
+
reactor.async do
|
95
|
+
input.wait_any(1)
|
53
96
|
input.io.write('Hello World')
|
54
97
|
end
|
55
98
|
|
56
|
-
output.
|
99
|
+
expect(output.wait_readable(1)).to be_truthy
|
100
|
+
end
|
101
|
+
|
102
|
+
it "can wait for readability in one task and writability in another" do
|
103
|
+
reactor.async do
|
104
|
+
expect do
|
105
|
+
input.wait_readable(1)
|
106
|
+
end.to raise_error(Async::Wrapper::Cancelled)
|
107
|
+
end
|
108
|
+
|
109
|
+
expect(input.monitor.interests).to be == :r
|
57
110
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
111
|
+
reactor.async do
|
112
|
+
input.wait_writable
|
113
|
+
|
114
|
+
input.close
|
115
|
+
output.close
|
116
|
+
end.wait
|
117
|
+
end
|
118
|
+
|
119
|
+
it "fails if waiting on from multiple tasks" do
|
120
|
+
input.reactor = reactor
|
121
|
+
|
122
|
+
reactor.async do
|
123
|
+
expect do
|
124
|
+
input.wait_readable
|
125
|
+
end.to raise_error(Async::Wrapper::Cancelled)
|
62
126
|
end
|
63
127
|
|
64
|
-
input.
|
65
|
-
|
128
|
+
expect(input.monitor.interests).to be == :r
|
129
|
+
|
130
|
+
reactor.async do
|
131
|
+
expect do
|
132
|
+
input.wait_readable
|
133
|
+
end.to raise_error(Async::Wrapper::WaitError)
|
134
|
+
end
|
66
135
|
end
|
67
136
|
end
|
68
137
|
|
69
138
|
describe '#reactor=' do
|
70
|
-
include_context Async::RSpec::Reactor
|
71
|
-
|
72
139
|
it 'can assign a wrapper to a reactor' do
|
73
140
|
input.reactor = reactor
|
74
141
|
|
75
142
|
expect(input.reactor).to be == reactor
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'assigns current reactor when waiting for events' do
|
146
|
+
input.wait_writable
|
76
147
|
|
148
|
+
expect(input.reactor).to be == reactor
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#close' do
|
153
|
+
it "closes monitor when closing wrapper" do
|
154
|
+
input.wait_writable
|
155
|
+
expect(input.monitor).to_not be_nil
|
156
|
+
input.close
|
157
|
+
expect(input.monitor).to be_nil
|
158
|
+
end
|
159
|
+
|
160
|
+
it "can't wait on closed wrapper" do
|
77
161
|
input.close
|
78
162
|
output.close
|
163
|
+
|
164
|
+
expect do
|
165
|
+
output.wait_readable
|
166
|
+
end.to raise_error(IOError, /closed stream/)
|
79
167
|
end
|
80
168
|
end
|
81
169
|
end
|
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.8.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-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -120,7 +120,8 @@ files:
|
|
120
120
|
- lib/async.rb
|
121
121
|
- lib/async/clock.rb
|
122
122
|
- lib/async/condition.rb
|
123
|
-
- lib/async/debug.rb
|
123
|
+
- lib/async/debug/monitor.rb
|
124
|
+
- lib/async/debug/selector.rb
|
124
125
|
- lib/async/logger.rb
|
125
126
|
- lib/async/measure.rb
|
126
127
|
- lib/async/node.rb
|