async 1.25.2 → 1.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/barrier.rb +1 -1
  3. data/lib/async/clock.rb +33 -1
  4. data/lib/async/logger.rb +1 -6
  5. data/lib/async/node.rb +20 -2
  6. data/lib/async/queue.rb +5 -1
  7. data/lib/async/reactor.rb +73 -12
  8. data/lib/async/scheduler.rb +112 -0
  9. data/lib/async/task.rb +11 -3
  10. data/lib/async/version.rb +1 -1
  11. metadata +46 -104
  12. data/.editorconfig +0 -6
  13. data/.github/workflows/development.yml +0 -55
  14. data/.gitignore +0 -14
  15. data/.rspec +0 -3
  16. data/.yardopts +0 -1
  17. data/Gemfile +0 -20
  18. data/Guardfile +0 -14
  19. data/README.md +0 -385
  20. data/Rakefile +0 -40
  21. data/async.gemspec +0 -34
  22. data/bake.rb +0 -33
  23. data/benchmark/async_vs_lightio.rb +0 -84
  24. data/benchmark/fiber_count.rb +0 -10
  25. data/benchmark/rubies/README.md +0 -51
  26. data/benchmark/rubies/benchmark.rb +0 -220
  27. data/benchmark/thread_count.rb +0 -9
  28. data/benchmark/thread_vs_fiber.rb +0 -45
  29. data/examples/async_method.rb +0 -60
  30. data/examples/callback/loop.rb +0 -44
  31. data/examples/capture/README.md +0 -59
  32. data/examples/capture/capture.rb +0 -116
  33. data/examples/fibers.rb +0 -178
  34. data/examples/queue/producer.rb +0 -28
  35. data/examples/sleep_sort.rb +0 -40
  36. data/examples/stop/condition.rb +0 -31
  37. data/examples/stop/sleep.rb +0 -42
  38. data/gems/event.gemfile +0 -4
  39. data/logo.png +0 -0
  40. data/logo.svg +0 -64
  41. data/papers/1982 Grossman.pdf +0 -0
  42. data/papers/1987 ODell.pdf +0 -0
  43. data/spec/async/barrier_spec.rb +0 -116
  44. data/spec/async/chainable_async_examples.rb +0 -13
  45. data/spec/async/clock_spec.rb +0 -37
  46. data/spec/async/condition_examples.rb +0 -105
  47. data/spec/async/condition_spec.rb +0 -72
  48. data/spec/async/logger_spec.rb +0 -65
  49. data/spec/async/node_spec.rb +0 -193
  50. data/spec/async/notification_spec.rb +0 -66
  51. data/spec/async/performance_spec.rb +0 -72
  52. data/spec/async/queue_spec.rb +0 -129
  53. data/spec/async/reactor/nested_spec.rb +0 -52
  54. data/spec/async/reactor_spec.rb +0 -253
  55. data/spec/async/semaphore_spec.rb +0 -169
  56. data/spec/async/task_spec.rb +0 -476
  57. data/spec/async/wrapper_spec.rb +0 -203
  58. data/spec/async_spec.rb +0 -33
  59. data/spec/enumerator_spec.rb +0 -83
  60. data/spec/kernel/async_spec.rb +0 -33
  61. data/spec/kernel/sync_spec.rb +0 -54
  62. data/spec/spec_helper.rb +0 -18
@@ -1,129 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async'
24
- require 'async/queue'
25
- require 'async/rspec'
26
- require 'async/semaphore'
27
-
28
- require_relative 'condition_examples'
29
- require_relative 'chainable_async_examples'
30
-
31
- RSpec.shared_context Async::Queue do
32
- it 'should process items in order' do
33
- reactor.async do |task|
34
- 10.times do |i|
35
- task.sleep(0.001)
36
- subject.enqueue(i)
37
- end
38
- end
39
-
40
- 10.times do |j|
41
- expect(subject.dequeue).to be == j
42
- end
43
- end
44
-
45
- it 'can dequeue items asynchronously' do
46
- reactor.async do |task|
47
- subject << 1
48
- subject << nil
49
- end
50
-
51
- subject.async do |task, item|
52
- expect(item).to be 1
53
- end
54
- end
55
-
56
- context 'with semaphore' do
57
- let(:capacity) {2}
58
- let(:semaphore) {Async::Semaphore.new(capacity)}
59
- let(:repeats) {capacity * 2}
60
-
61
- it 'should process several items limited by a semaphore' do
62
- count = 0
63
-
64
- Async do
65
- repeats.times do
66
- subject.enqueue :item
67
- end
68
-
69
- subject.enqueue nil
70
- end
71
-
72
- subject.async(parent: semaphore) do |task|
73
- count += 1
74
- end
75
-
76
- expect(count).to be == repeats
77
- end
78
- end
79
-
80
- it_behaves_like 'chainable async' do
81
- before do
82
- subject.enqueue(:item)
83
-
84
- # The limited queue may block.
85
- Async do
86
- subject.enqueue(nil)
87
- end
88
- end
89
- end
90
- end
91
-
92
- RSpec.describe Async::Queue do
93
- include_context Async::RSpec::Reactor
94
-
95
- it_behaves_like Async::Queue
96
- end
97
-
98
- RSpec.describe Async::LimitedQueue do
99
- include_context Async::RSpec::Reactor
100
-
101
- it_behaves_like Async::Queue
102
-
103
- it 'should become limited' do
104
- expect(subject).to_not be_limited
105
- subject.enqueue(10)
106
- expect(subject).to be_limited
107
- end
108
-
109
- it 'should resume waiting tasks in order' do
110
- total_resumed = 0
111
- total_dequeued = 0
112
-
113
- Async do |producer|
114
- 10.times do
115
- producer.async do
116
- subject.enqueue('foo')
117
- total_resumed += 1
118
- end
119
- end
120
- end
121
-
122
- 10.times do
123
- item = subject.dequeue
124
- total_dequeued += 1
125
-
126
- expect(total_resumed).to be == total_dequeued
127
- end
128
- end
129
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- RSpec.describe Async::Reactor do
24
- describe '::run (in existing reactor)' do
25
- include_context Async::RSpec::Reactor
26
-
27
- it "should nest reactor" do
28
- outer_reactor = Async::Task.current.reactor
29
- inner_reactor = nil
30
-
31
- described_class.run do |task|
32
- inner_reactor = task.reactor
33
- end
34
-
35
- expect(outer_reactor).to be_kind_of(described_class)
36
- expect(outer_reactor).to be_eql(inner_reactor)
37
- end
38
- end
39
-
40
- describe '::run' do
41
- it "should nest reactor" do
42
- expect(Async::Task.current?).to be_nil
43
- inner_reactor = nil
44
-
45
- described_class.run do |task|
46
- inner_reactor = task.reactor
47
- end
48
-
49
- expect(inner_reactor).to be_kind_of(described_class)
50
- end
51
- end
52
- end
@@ -1,253 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async'
24
- require 'async/rspec/reactor'
25
-
26
- require 'benchmark/ips'
27
-
28
- RSpec.describe Async::Reactor do
29
- describe '#run' do
30
- it "can run tasks on different fibers" do
31
- outer_fiber = Fiber.current
32
- inner_fiber = nil
33
-
34
- described_class.run do |task|
35
- task.sleep(0)
36
- inner_fiber = Fiber.current
37
- end
38
-
39
- expect(inner_fiber).to_not be nil
40
- expect(outer_fiber).to_not be == inner_fiber
41
- end
42
- end
43
-
44
- describe '#close' do
45
- it "can close empty reactor" do
46
- subject.close
47
-
48
- expect(subject).to be_closed
49
- end
50
- end
51
-
52
- describe '#run_once' do
53
- it "can run the reactor" do
54
- # Run the reactor for 1 second:
55
- task = subject.async do |task|
56
- task.yield
57
- end
58
-
59
- expect(task).to be_running
60
-
61
- # This will resume the task, and then the reactor will be finished.
62
- expect(subject.run_once).to be false
63
-
64
- expect(task).to be_finished
65
- end
66
-
67
- it "can run one iteration" do
68
- state = nil
69
-
70
- subject.async do |task|
71
- state = :started
72
- task.yield
73
- state = :finished
74
- end
75
-
76
- expect(state).to be :started
77
-
78
- subject.run_once
79
- expect(state).to be :finished
80
- end
81
- end
82
-
83
- describe '#print_hierarchy' do
84
- it "can print hierarchy" do
85
- subject.async do |parent|
86
- parent.async do |child|
87
- child.sleep 1
88
- end
89
-
90
- parent.sleep 1
91
- end
92
-
93
- output = StringIO.new
94
- subject.print_hierarchy(output)
95
- lines = output.string.lines
96
-
97
- expect(lines[0]).to be =~ /#<Async::Reactor.*(running)/
98
- expect(lines[1]).to be =~ /\t#<Async::Task.*(running)/
99
- expect(lines[2]).to be =~ /\t\t#<Async::Task.*(running)/
100
- end
101
- end
102
-
103
- describe '#stop' do
104
- it "can stop the reactor" do
105
- state = nil
106
-
107
- subject.async(annotation: "sleep(10)") do |task|
108
- state = :started
109
- task.sleep(10)
110
- state = :stopped
111
- end
112
-
113
- subject.async(annotation: "reactor.stop") do |task|
114
- task.sleep(0.1)
115
- task.reactor.stop
116
- end
117
-
118
- subject.run
119
-
120
- expect(state).to be == :started
121
-
122
- subject.close
123
- end
124
-
125
- it "can stop reactor from different thread" do
126
- events = Thread::Queue.new
127
-
128
- thread = Thread.new do
129
- if events.pop
130
- subject.stop
131
- end
132
- end
133
-
134
- subject.async do |task|
135
- events << true
136
- end
137
-
138
- subject.run
139
-
140
- thread.join
141
-
142
- expect(subject).to be_stopped
143
- end
144
- end
145
-
146
- it "can't return" do
147
- expect do
148
- Async do |task|
149
- return
150
- end.wait
151
- end.to raise_exception(LocalJumpError)
152
- end
153
-
154
- it "is closed after running" do
155
- reactor = nil
156
-
157
- Async do |task|
158
- reactor = task.reactor
159
- end
160
-
161
- expect(reactor).to be_closed
162
-
163
- expect{reactor.run}.to raise_exception(RuntimeError, /closed/)
164
- end
165
-
166
- it "should return a task" do
167
- result = Async do |task|
168
- end
169
-
170
- expect(result).to be_kind_of(Async::Task)
171
- end
172
-
173
- describe '#async' do
174
- include_context Async::RSpec::Reactor
175
-
176
- it "can pass in arguments" do
177
- reactor.async(:arg) do |task, arg|
178
- expect(arg).to be == :arg
179
- end.wait
180
- end
181
-
182
- it "passes in the correct number of arguments" do
183
- reactor.async(:arg1, :arg2, :arg3) do |task, arg1, arg2, arg3|
184
- expect(arg1).to be == :arg1
185
- expect(arg2).to be == :arg2
186
- expect(arg3).to be == :arg3
187
- end.wait
188
- end
189
- end
190
-
191
- describe '#with_timeout' do
192
- let(:duration) {1}
193
-
194
- it "stops immediately" do
195
- start_time = Time.now
196
-
197
- described_class.run do |task|
198
- condition = Async::Condition.new
199
-
200
- task.with_timeout(duration) do
201
- task.async do
202
- condition.wait
203
- end
204
-
205
- condition.signal
206
-
207
- task.yield
208
-
209
- task.children.each(&:wait)
210
- end
211
- end
212
-
213
- duration = Time.now - start_time
214
-
215
- expect(duration).to be < 0.1
216
- end
217
-
218
- let(:timeout_class) {Class.new(RuntimeError)}
219
-
220
- it "raises specified exception" do
221
- expect do
222
- described_class.run do |task|
223
- task.with_timeout(0.0, timeout_class) do
224
- task.sleep(1.0)
225
- end
226
- end.wait
227
- end.to raise_exception(timeout_class)
228
- end
229
-
230
- it "should be fast to use timeouts" do
231
- Benchmark.ips do |x|
232
- x.report('Reactor#with_timeout') do |repeats|
233
- Async do |task|
234
- reactor = task.reactor
235
-
236
- repeats.times do
237
- reactor.with_timeout(1) do
238
- end
239
- end
240
- end
241
- end
242
-
243
- x.compare!
244
- end
245
- end
246
- end
247
-
248
- describe '#to_s' do
249
- it "shows stopped=" do
250
- expect(subject.to_s).to include "stopped"
251
- end
252
- end
253
- end
@@ -1,169 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async/semaphore'
24
- require 'async/barrier'
25
- require 'async/rspec'
26
-
27
- require_relative 'chainable_async_examples'
28
-
29
- RSpec.describe Async::Semaphore do
30
- include_context Async::RSpec::Reactor
31
-
32
- context '#async' do
33
- let(:repeats) {40}
34
- let(:limit) {4}
35
-
36
- it 'should process work in batches' do
37
- semaphore = Async::Semaphore.new(limit)
38
- current, maximum = 0, 0
39
-
40
- result = repeats.times.map do |i|
41
- semaphore.async do |task|
42
- current += 1
43
- maximum = [current, maximum].max
44
- task.sleep(rand * 0.1)
45
- current -= 1
46
-
47
- i
48
- end
49
- end.collect(&:result)
50
-
51
- # Verify that the maximum number of concurrent tasks was the specificed limit:
52
- expect(maximum).to be == limit
53
-
54
- # Verify that the results were in the correct order:
55
- expect(result).to be == (0...repeats).to_a
56
- end
57
-
58
- it 'only allows one task at a time' do
59
- semaphore = Async::Semaphore.new(1)
60
- order = []
61
-
62
- 3.times.map do |i|
63
- semaphore.async do |task|
64
- order << i
65
- task.sleep(0.1)
66
- order << i
67
- end
68
- end.collect(&:result)
69
-
70
- expect(order).to be == [0, 0, 1, 1, 2, 2]
71
- end
72
-
73
- it 'allows tasks to execute concurrently' do
74
- semaphore = Async::Semaphore.new(3)
75
- order = []
76
-
77
- 3.times.map do |i|
78
- semaphore.async do |task|
79
- order << i
80
- task.sleep(0.1)
81
- order << i
82
- end
83
- end.collect(&:result)
84
-
85
- expect(order).to be == [0, 1, 2, 0, 1, 2]
86
- end
87
- end
88
-
89
- context '#waiting' do
90
- subject {Async::Semaphore.new(0)}
91
- it 'handles exceptions thrown while waiting' do
92
- expect do
93
- reactor.with_timeout(0.1) do
94
- subject.acquire do
95
- end
96
- end
97
- end.to raise_error(Async::TimeoutError)
98
-
99
- expect(subject.waiting).to be_empty
100
- end
101
- end
102
-
103
- context '#count' do
104
- it 'should count number of current acquisitions' do
105
- expect(subject.count).to be == 0
106
-
107
- subject.acquire do
108
- expect(subject.count).to be == 1
109
- end
110
- end
111
- end
112
-
113
- context '#limit' do
114
- it 'should have a default limit' do
115
- expect(subject.limit).to be == 1
116
- end
117
- end
118
-
119
- context '#empty?' do
120
- it 'should be empty unless acquired' do
121
- expect(subject).to be_empty
122
-
123
- subject.acquire do
124
- expect(subject).to_not be_empty
125
- end
126
- end
127
- end
128
-
129
- context '#blocking?' do
130
- it 'will be blocking when acquired' do
131
- expect(subject).to_not be_blocking
132
-
133
- subject.acquire do
134
- expect(subject).to be_blocking
135
- end
136
- end
137
- end
138
-
139
- context '#acquire/#release' do
140
- it 'works when called without block' do
141
- subject.acquire
142
-
143
- expect(subject.count).to be == 1
144
-
145
- subject.release
146
-
147
- expect(subject.count).to be == 0
148
- end
149
- end
150
-
151
- context 'with barrier' do
152
- let(:capacity) {2}
153
- let(:barrier) {Async::Barrier.new}
154
- let(:repeats) {capacity * 2}
155
-
156
- it 'should execute several tasks and wait using a barrier' do
157
- repeats.times do
158
- subject.async(parent: barrier) do |task|
159
- task.sleep 0.1
160
- end
161
- end
162
-
163
- expect(barrier.size).to be == repeats
164
- barrier.wait
165
- end
166
- end
167
-
168
- it_behaves_like 'chainable async'
169
- end