async 1.12.0 → 1.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/README.md +207 -18
- data/Rakefile +1 -0
- data/lib/async.rb +7 -1
- data/lib/async/logger.rb +131 -2
- data/lib/async/node.rb +3 -3
- data/lib/async/queue.rb +2 -0
- data/lib/async/reactor.rb +8 -8
- data/lib/async/task.rb +64 -34
- data/lib/async/terminal.rb +99 -0
- data/lib/async/version.rb +1 -1
- data/lib/async/wrapper.rb +1 -5
- data/spec/async/condition_examples.rb +1 -1
- data/spec/async/logger_spec.rb +48 -4
- data/spec/async/performance_spec.rb +2 -2
- data/spec/async/reactor/nested_spec.rb +4 -4
- data/spec/async/reactor_spec.rb +8 -8
- data/spec/async/task_spec.rb +91 -19
- data/spec/async/wrapper_spec.rb +11 -9
- data/spec/enumerator_spec.rb +5 -5
- metadata +4 -3
@@ -26,9 +26,9 @@ RSpec.describe Async::Reactor do
|
|
26
26
|
outer_reactor = Async::Task.current.reactor
|
27
27
|
inner_reactor = nil
|
28
28
|
|
29
|
-
|
29
|
+
described_class.run do |task|
|
30
30
|
inner_reactor = task.reactor
|
31
|
-
end
|
31
|
+
end
|
32
32
|
|
33
33
|
expect(outer_reactor).to be_kind_of(described_class)
|
34
34
|
expect(outer_reactor).to be_eql(inner_reactor)
|
@@ -40,9 +40,9 @@ RSpec.describe Async::Reactor do
|
|
40
40
|
expect(Async::Task.current?).to be_nil
|
41
41
|
inner_reactor = nil
|
42
42
|
|
43
|
-
|
43
|
+
described_class.run do |task|
|
44
44
|
inner_reactor = task.reactor
|
45
|
-
end
|
45
|
+
end
|
46
46
|
|
47
47
|
expect(inner_reactor).to be_kind_of(described_class)
|
48
48
|
end
|
data/spec/async/reactor_spec.rb
CHANGED
@@ -75,26 +75,26 @@ RSpec.describe Async::Reactor do
|
|
75
75
|
end
|
76
76
|
it "can't return" do
|
77
77
|
expect do
|
78
|
-
Async
|
78
|
+
Async do |task|
|
79
79
|
return
|
80
|
-
end
|
81
|
-
end.to
|
80
|
+
end.wait
|
81
|
+
end.to raise_exception(LocalJumpError)
|
82
82
|
end
|
83
83
|
|
84
84
|
it "is closed after running" do
|
85
85
|
reactor = nil
|
86
86
|
|
87
|
-
Async
|
87
|
+
Async do |task|
|
88
88
|
reactor = task.reactor
|
89
89
|
end
|
90
90
|
|
91
91
|
expect(reactor).to be_closed
|
92
92
|
|
93
|
-
expect{reactor.run}.to
|
93
|
+
expect{reactor.run}.to raise_exception(RuntimeError, /closed/)
|
94
94
|
end
|
95
95
|
|
96
96
|
it "should return a task" do
|
97
|
-
result = Async
|
97
|
+
result = Async do |task|
|
98
98
|
end
|
99
99
|
|
100
100
|
expect(result).to be_kind_of(Async::Task)
|
@@ -153,8 +153,8 @@ RSpec.describe Async::Reactor do
|
|
153
153
|
task.timeout(0.0, timeout_class) do
|
154
154
|
task.sleep(1.0)
|
155
155
|
end
|
156
|
-
end
|
157
|
-
end.to
|
156
|
+
end.wait
|
157
|
+
end.to raise_exception(timeout_class)
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
data/spec/async/task_spec.rb
CHANGED
@@ -28,7 +28,7 @@ RSpec.describe Async::Task do
|
|
28
28
|
task = reactor.async do |task|
|
29
29
|
end
|
30
30
|
|
31
|
-
expect{task.run}.to
|
31
|
+
expect{task.run}.to raise_exception(RuntimeError, /already running/)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -54,13 +54,61 @@ RSpec.describe Async::Task do
|
|
54
54
|
end.wait
|
55
55
|
end.wait
|
56
56
|
end
|
57
|
+
|
58
|
+
it "can raise exceptions" do
|
59
|
+
expect do
|
60
|
+
reactor.async do |task|
|
61
|
+
raise "boom"
|
62
|
+
end.wait
|
63
|
+
end.to raise_exception RuntimeError, /boom/
|
64
|
+
end
|
65
|
+
|
66
|
+
it "can raise exception after asynchronous operation" do
|
67
|
+
task = nil
|
68
|
+
|
69
|
+
expect do
|
70
|
+
task = reactor.async do |task|
|
71
|
+
task.sleep 0.1
|
72
|
+
|
73
|
+
raise "boom"
|
74
|
+
end
|
75
|
+
end.to_not raise_exception
|
76
|
+
|
77
|
+
reactor.run do
|
78
|
+
expect do
|
79
|
+
task.wait
|
80
|
+
end.to raise_exception RuntimeError, /boom/
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "can consume exceptions" do
|
85
|
+
task = nil
|
86
|
+
|
87
|
+
expect do
|
88
|
+
task = reactor.async(propagate_exceptions: false) do |task|
|
89
|
+
raise "boom"
|
90
|
+
end
|
91
|
+
end.to_not raise_exception
|
92
|
+
|
93
|
+
expect do
|
94
|
+
task.wait
|
95
|
+
end.to raise_exception RuntimeError, /boom/
|
96
|
+
end
|
97
|
+
|
98
|
+
it "won't consume non-StandardError exceptions" do
|
99
|
+
expect do
|
100
|
+
reactor.async(propagate_exceptions: false) do |task|
|
101
|
+
raise SignalException.new(:TERM)
|
102
|
+
end
|
103
|
+
end.to raise_exception(SignalException, /TERM/)
|
104
|
+
end
|
57
105
|
end
|
58
106
|
|
59
107
|
describe '#yield' do
|
60
108
|
it "can yield back to reactor" do
|
61
109
|
state = nil
|
62
110
|
|
63
|
-
|
111
|
+
reactor.async do |task|
|
64
112
|
state = :started
|
65
113
|
task.yield
|
66
114
|
state = :finished
|
@@ -90,7 +138,7 @@ RSpec.describe Async::Task do
|
|
90
138
|
it "should kill direct child" do
|
91
139
|
parent_task = child_task = nil
|
92
140
|
|
93
|
-
|
141
|
+
reactor.async do |task|
|
94
142
|
parent_task = task
|
95
143
|
reactor.async do |task|
|
96
144
|
child_task = task
|
@@ -135,7 +183,7 @@ RSpec.describe Async::Task do
|
|
135
183
|
it "can sleep for the requested duration" do
|
136
184
|
state = nil
|
137
185
|
|
138
|
-
|
186
|
+
reactor.async do |task|
|
139
187
|
task.sleep(duration)
|
140
188
|
state = :finished
|
141
189
|
end
|
@@ -152,10 +200,26 @@ RSpec.describe Async::Task do
|
|
152
200
|
end
|
153
201
|
|
154
202
|
describe '#timeout' do
|
203
|
+
it "can extend timeout" do
|
204
|
+
reactor.async do |task|
|
205
|
+
task.timeout(0.2) do |timer|
|
206
|
+
task.sleep(0.1)
|
207
|
+
|
208
|
+
expect(timer.fires_in).to be_within(10).percent_of(0.1)
|
209
|
+
|
210
|
+
timer.reset
|
211
|
+
|
212
|
+
expect(timer.fires_in).to be_within(10).percent_of(0.2)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
reactor.run
|
217
|
+
end
|
218
|
+
|
155
219
|
it "will timeout if execution takes too long" do
|
156
220
|
state = nil
|
157
221
|
|
158
|
-
|
222
|
+
reactor.async do |task|
|
159
223
|
begin
|
160
224
|
task.timeout(0.01) do
|
161
225
|
state = :started
|
@@ -175,7 +239,7 @@ RSpec.describe Async::Task do
|
|
175
239
|
it "won't timeout if execution completes in time" do
|
176
240
|
state = nil
|
177
241
|
|
178
|
-
|
242
|
+
reactor.async do |task|
|
179
243
|
state = :started
|
180
244
|
task.timeout(0.01) do
|
181
245
|
task.sleep(0.001)
|
@@ -191,8 +255,6 @@ RSpec.describe Async::Task do
|
|
191
255
|
|
192
256
|
describe '#wait' do
|
193
257
|
it "will wait on another task to complete" do
|
194
|
-
result = nil
|
195
|
-
|
196
258
|
apples_task = reactor.async do |task|
|
197
259
|
task.sleep(0.1)
|
198
260
|
|
@@ -205,33 +267,43 @@ RSpec.describe Async::Task do
|
|
205
267
|
:oranges
|
206
268
|
end
|
207
269
|
|
208
|
-
|
209
|
-
|
270
|
+
fruit_salad = reactor.async do |task|
|
271
|
+
[apples_task.wait, oranges_task.wait]
|
210
272
|
end
|
211
273
|
|
212
274
|
reactor.run
|
213
275
|
|
214
|
-
expect(
|
276
|
+
expect(fruit_salad.wait).to be == [:apples, :oranges]
|
277
|
+
end
|
278
|
+
|
279
|
+
it "will raise exceptions when checking result" do
|
280
|
+
error_task = nil
|
281
|
+
|
282
|
+
error_task = reactor.async(propagate_exceptions: false) do |task|
|
283
|
+
raise RuntimeError, "brain not provided"
|
284
|
+
end
|
285
|
+
|
286
|
+
expect do
|
287
|
+
error_task.wait
|
288
|
+
end.to raise_exception(RuntimeError, /brain/)
|
215
289
|
end
|
216
290
|
|
217
|
-
it "will propagate exceptions" do
|
291
|
+
it "will propagate exceptions after async operation" do
|
218
292
|
error_task = nil
|
219
293
|
|
220
|
-
error_task = reactor.async do |task|
|
294
|
+
error_task = reactor.async(propagate_exceptions: false) do |task|
|
221
295
|
task.sleep(0.1)
|
222
296
|
|
223
|
-
raise
|
297
|
+
raise "boom"
|
224
298
|
end
|
225
299
|
|
226
300
|
innocent_task = reactor.async do |task|
|
227
|
-
expect{error_task.
|
301
|
+
expect{error_task.wait}.to raise_exception RuntimeError, /boom/
|
228
302
|
end
|
229
303
|
|
230
|
-
|
304
|
+
expect do
|
231
305
|
reactor.run
|
232
|
-
|
233
|
-
retry
|
234
|
-
end
|
306
|
+
end.to_not raise_exception
|
235
307
|
|
236
308
|
expect(error_task).to be_finished
|
237
309
|
expect(innocent_task).to be_finished
|
data/spec/async/wrapper_spec.rb
CHANGED
@@ -46,7 +46,9 @@ RSpec.describe Async::Wrapper do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it "can timeout if no event occurs" do
|
49
|
-
expect
|
49
|
+
expect do
|
50
|
+
output.wait_readable(0.1)
|
51
|
+
end.to raise_exception(Async::TimeoutError)
|
50
52
|
end
|
51
53
|
|
52
54
|
it "can wait for readability in sequential tasks" do
|
@@ -66,7 +68,7 @@ RSpec.describe Async::Wrapper do
|
|
66
68
|
reactor.async do
|
67
69
|
expect do
|
68
70
|
output.wait_readable
|
69
|
-
end.to
|
71
|
+
end.to raise_exception(Async::Wrapper::Cancelled)
|
70
72
|
end
|
71
73
|
|
72
74
|
expect(output.monitor).to_not be_nil
|
@@ -82,7 +84,7 @@ RSpec.describe Async::Wrapper do
|
|
82
84
|
reactor.async do
|
83
85
|
expect do
|
84
86
|
input.wait_readable
|
85
|
-
end.to
|
87
|
+
end.to raise_exception(Async::Wrapper::Cancelled)
|
86
88
|
end
|
87
89
|
|
88
90
|
# This reproduces the race condition that can occur if two tasks are resumed in sequence.
|
@@ -93,14 +95,14 @@ RSpec.describe Async::Wrapper do
|
|
93
95
|
# Resume task 2:
|
94
96
|
expect do
|
95
97
|
output.resume
|
96
|
-
end.to_not
|
98
|
+
end.to_not raise_exception
|
97
99
|
end
|
98
100
|
|
99
101
|
it "can be cancelled" do
|
100
102
|
reactor.async do
|
101
103
|
expect do
|
102
104
|
input.wait_readable
|
103
|
-
end.to
|
105
|
+
end.to raise_exception(Async::Wrapper::Cancelled)
|
104
106
|
end
|
105
107
|
|
106
108
|
expect(input.monitor).to_not be_nil
|
@@ -121,7 +123,7 @@ RSpec.describe Async::Wrapper do
|
|
121
123
|
reactor.async do
|
122
124
|
expect do
|
123
125
|
input.wait_readable(1)
|
124
|
-
end.to
|
126
|
+
end.to raise_exception(Async::Wrapper::Cancelled)
|
125
127
|
end
|
126
128
|
|
127
129
|
expect(input.monitor.interests).to be == :r
|
@@ -140,7 +142,7 @@ RSpec.describe Async::Wrapper do
|
|
140
142
|
reactor.async do
|
141
143
|
expect do
|
142
144
|
input.wait_readable
|
143
|
-
end.to
|
145
|
+
end.to raise_exception(Async::Wrapper::Cancelled)
|
144
146
|
end
|
145
147
|
|
146
148
|
expect(input.monitor.interests).to be == :r
|
@@ -148,7 +150,7 @@ RSpec.describe Async::Wrapper do
|
|
148
150
|
reactor.async do
|
149
151
|
expect do
|
150
152
|
input.wait_readable
|
151
|
-
end.to
|
153
|
+
end.to raise_exception(Async::Wrapper::WaitError)
|
152
154
|
end
|
153
155
|
end
|
154
156
|
end
|
@@ -193,7 +195,7 @@ RSpec.describe Async::Wrapper do
|
|
193
195
|
|
194
196
|
expect do
|
195
197
|
output.wait_readable
|
196
|
-
end.to
|
198
|
+
end.to raise_exception(IOError, /closed stream/)
|
197
199
|
end
|
198
200
|
end
|
199
201
|
end
|
data/spec/enumerator_spec.rb
CHANGED
@@ -34,7 +34,7 @@ RSpec.describe Enumerator do
|
|
34
34
|
# no fiber really used in internal iterator,
|
35
35
|
# but let this test be here for completness
|
36
36
|
ar = nil
|
37
|
-
Async
|
37
|
+
Async do |task|
|
38
38
|
ar = enum(task).to_a
|
39
39
|
end
|
40
40
|
expect(ar).to be == [1, 2]
|
@@ -42,7 +42,7 @@ RSpec.describe Enumerator do
|
|
42
42
|
|
43
43
|
it "should play well with Enumerator as external iterator", pending: "expected failure" do
|
44
44
|
ar = []
|
45
|
-
Async
|
45
|
+
Async do |task|
|
46
46
|
en = enum(task)
|
47
47
|
ar << en.next
|
48
48
|
ar << en.next
|
@@ -54,15 +54,15 @@ RSpec.describe Enumerator do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should play well with Enumerator.zip(Enumerator) method", pending: "expected failure" do
|
57
|
-
Async
|
57
|
+
Async do |task|
|
58
58
|
ar = [:a, :b, :c, :d].each.zip(enum(task))
|
59
59
|
expect(ar).to be == [[:a, 1], [:b, 2], [:c, nil], [:d, nil]]
|
60
|
-
end
|
60
|
+
end.wait
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should play with explicit Fiber usage", pending: "expected failure" do
|
64
64
|
ar = []
|
65
|
-
Async
|
65
|
+
Async do |task|
|
66
66
|
fib = Fiber.new {
|
67
67
|
Fiber.yield 1
|
68
68
|
task.sleep(0.002)
|
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.13.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:
|
11
|
+
date: 2019-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- lib/async/reactor.rb
|
132
132
|
- lib/async/semaphore.rb
|
133
133
|
- lib/async/task.rb
|
134
|
+
- lib/async/terminal.rb
|
134
135
|
- lib/async/version.rb
|
135
136
|
- lib/async/wrapper.rb
|
136
137
|
- logo.png
|
@@ -171,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
172
|
- !ruby/object:Gem::Version
|
172
173
|
version: '0'
|
173
174
|
requirements: []
|
174
|
-
rubygems_version: 3.0.
|
175
|
+
rubygems_version: 3.0.1
|
175
176
|
signing_key:
|
176
177
|
specification_version: 4
|
177
178
|
summary: Async is an asynchronous I/O framework based on nio4r.
|