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,476 +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/clock'
25
-
26
- RSpec.describe Async::Task do
27
- let(:reactor) {Async::Reactor.new}
28
-
29
- describe '#run' do
30
- it "can't be invoked twice" do
31
- task = reactor.async do |task|
32
- end
33
-
34
- expect{task.run}.to raise_exception(RuntimeError, /already running/)
35
- end
36
- end
37
-
38
- describe '#current?' do
39
- it "can check if it is the currently running task" do
40
- task = reactor.async do |task|
41
- expect(task).to be_current
42
- task.sleep(0.1)
43
- end
44
-
45
- expect(task).to_not be_current
46
- end
47
- end
48
-
49
- describe '#async' do
50
- it "can start child async tasks" do
51
- child = nil
52
-
53
- parent = reactor.async do |task|
54
- child = task.async do
55
- task.sleep(1)
56
- end
57
- end
58
-
59
- expect(parent).to_not be_nil
60
- expect(child).to_not be_nil
61
- expect(child.parent).to_not be_nil
62
- end
63
-
64
- it "can pass in arguments" do
65
- reactor.async do |task|
66
- task.async(:arg) do |task, arg|
67
- expect(arg).to be == :arg
68
- end.wait
69
- end.wait
70
- end
71
-
72
- it "can set initial annotation" do
73
- reactor.async(annotation: "Hello World") do |task|
74
- expect(task.annotation).to be == "Hello World"
75
- end.wait
76
- end
77
-
78
- it "can raise exceptions" do
79
- expect do
80
- reactor.async do |task|
81
- raise "boom"
82
- end.wait
83
- end.to raise_exception RuntimeError, /boom/
84
- end
85
-
86
- it "can raise exception after asynchronous operation" do
87
- task = nil
88
-
89
- expect do
90
- task = reactor.async do |task|
91
- task.sleep 0.1
92
-
93
- raise "boom"
94
- end
95
- end.to_not raise_exception
96
-
97
- reactor.run do
98
- expect do
99
- task.wait
100
- end.to raise_exception RuntimeError, /boom/
101
- end
102
- end
103
-
104
- it "can consume exceptions" do
105
- task = nil
106
-
107
- expect do
108
- task = reactor.async do |task|
109
- raise "boom"
110
- end
111
- end.to_not raise_exception
112
-
113
- expect do
114
- task.wait
115
- end.to raise_exception RuntimeError, /boom/
116
- end
117
-
118
- it "won't consume non-StandardError exceptions" do
119
- expect do
120
- reactor.async do |task|
121
- raise SignalException.new(:TERM)
122
- end
123
- end.to raise_exception(SignalException, /TERM/)
124
- end
125
- end
126
-
127
- describe '#yield' do
128
- it "can yield back to reactor" do
129
- state = nil
130
-
131
- reactor.async do |task|
132
- state = :started
133
- task.yield
134
- state = :finished
135
- end
136
-
137
- reactor.run
138
-
139
- expect(state).to be == :finished
140
- end
141
- end
142
-
143
- describe '#stop' do
144
- it "can be stopped" do
145
- state = nil
146
-
147
- task = reactor.async do |task|
148
- state = :started
149
- task.sleep(10)
150
- state = :finished
151
- end
152
-
153
- task.stop
154
-
155
- expect(state).to be == :started
156
- expect(task).to be_stopped
157
- end
158
-
159
- it "can stop nested tasks with exception handling" do
160
- task = reactor.async do |task|
161
- child = task.async do |subtask|
162
- subtask.sleep(1)
163
- end
164
-
165
- begin
166
- child.wait
167
- ensure
168
- child.stop
169
- end
170
- end
171
-
172
- subtask = task.children.first
173
- task.stop
174
-
175
- expect(task.status).to be :stopped
176
- expect(subtask.status).to be :stopped
177
- end
178
-
179
- it "can stop current task" do
180
- state = nil
181
-
182
- task = reactor.async do |task|
183
- state = :started
184
- task.stop
185
- state = :finished
186
- end
187
-
188
- expect(state).to be == :started
189
- expect(task).to be_stopped
190
- end
191
-
192
- it "can stop current task using exception" do
193
- state = nil
194
-
195
- task = reactor.async do |task|
196
- state = :started
197
- raise Async::Stop, "I'm finished."
198
- state = :finished
199
- end
200
-
201
- expect(state).to be == :started
202
- expect(task).to be_stopped
203
- end
204
-
205
- it "should stop direct child" do
206
- parent_task = child_task = nil
207
-
208
- reactor.async do |task|
209
- parent_task = task
210
- reactor.async do |task|
211
- child_task = task
212
- task.sleep(10)
213
- end
214
- task.sleep(10)
215
- end
216
-
217
- expect(parent_task).to_not be_nil
218
- expect(child_task).to_not be_nil
219
-
220
- expect(parent_task.fiber).to be_alive
221
- expect(child_task.fiber).to be_alive
222
-
223
- parent_task.stop
224
-
225
- expect(parent_task).to_not be_alive
226
- expect(child_task).to_not be_alive
227
- end
228
-
229
- it "can stop nested parent" do
230
- parent_task = nil
231
- children_tasks = []
232
-
233
- reactor.async do |task|
234
- parent_task = task
235
-
236
- reactor.async do |task|
237
- children_tasks << task
238
- task.sleep(2)
239
- end
240
-
241
- reactor.async do |task|
242
- children_tasks << task
243
- task.sleep(1)
244
- parent_task.stop
245
- end
246
-
247
- reactor.async do |task|
248
- children_tasks << task
249
- task.sleep(2)
250
- end
251
- end
252
-
253
- reactor.run
254
-
255
- expect(parent_task).to_not be_alive
256
- end
257
-
258
- it "should not remove running task" do
259
- top_task = middle_task = bottom_task = nil
260
-
261
- top_task = reactor.async do |task|
262
- middle_task = reactor.async do |task|
263
- bottom_task = reactor.async do |task|
264
- task.sleep(10)
265
- end
266
- task.sleep(10)
267
- end
268
- task.sleep(10)
269
- end
270
-
271
- bottom_task.stop
272
- expect(top_task.children).to include(middle_task)
273
- end
274
-
275
- it "can stop resumed task" do
276
- items = [1, 2, 3]
277
-
278
- Async do
279
- condition = Async::Condition.new
280
-
281
- producer = Async do |subtask|
282
- while item = items.pop
283
- subtask.yield # (1) Fiber.yield, (3) Reactor -> producer.resume
284
- condition.signal(item) # (4) consumer.resume(value)
285
- end
286
- end
287
-
288
- value = condition.wait # (2) value = Fiber.yield
289
- expect(value).to be == 3
290
- producer.stop # (5) [producer is resumed already] producer.stop
291
- end
292
-
293
- expect(items).to be == [1]
294
- end
295
- end
296
-
297
- describe '#sleep' do
298
- let(:duration) {0.01}
299
-
300
- it "can sleep for the requested duration" do
301
- state = nil
302
-
303
- reactor.async do |task|
304
- task.sleep(duration)
305
- state = :finished
306
- end
307
-
308
- time = Async::Clock.measure do
309
- reactor.run
310
- end
311
-
312
- # This is too unstable on travis.
313
- # expect(time).to be_within(50).percent_of(duration)
314
- expect(time).to be >= duration
315
- expect(state).to be == :finished
316
- end
317
- end
318
-
319
- describe '#with_timeout' do
320
- it "can extend timeout" do
321
- reactor.async do |task|
322
- task.with_timeout(0.2) do |timer|
323
- task.sleep(0.1)
324
-
325
- expect(timer.fires_in).to be_within(10 * Q).percent_of(0.1)
326
-
327
- timer.reset
328
-
329
- expect(timer.fires_in).to be_within(10 * Q).percent_of(0.2)
330
- end
331
- end
332
-
333
- reactor.run
334
- end
335
-
336
- it "will timeout if execution takes too long" do
337
- state = nil
338
-
339
- reactor.async do |task|
340
- begin
341
- task.with_timeout(0.01) do
342
- state = :started
343
- task.sleep(10)
344
- state = :finished
345
- end
346
- rescue Async::TimeoutError
347
- state = :timeout
348
- end
349
- end
350
-
351
- reactor.run
352
-
353
- expect(state).to be == :timeout
354
- end
355
-
356
- it "won't timeout if execution completes in time" do
357
- state = nil
358
-
359
- reactor.async do |task|
360
- state = :started
361
- task.with_timeout(0.01) do
362
- task.sleep(0.001)
363
- state = :finished
364
- end
365
- end
366
-
367
- reactor.run
368
-
369
- expect(state).to be == :finished
370
- end
371
-
372
- def sleep_forever
373
- while true
374
- Async::Task.current.sleep(1)
375
- end
376
- end
377
-
378
- it "contains useful backtrace" do
379
- task = Async do |task|
380
- task.with_timeout(1.0) do
381
- sleep_forever
382
- end
383
- end
384
-
385
- expect{task.wait}.to raise_error(Async::TimeoutError)
386
-
387
- # TODO replace this with task.result
388
- task.wait rescue error = $!
389
- expect(error.backtrace).to include(/sleep_forever/)
390
- end
391
- end
392
-
393
- describe '#wait' do
394
- it "will wait on another task to complete" do
395
- apples_task = reactor.async do |task|
396
- task.sleep(0.1)
397
-
398
- :apples
399
- end
400
-
401
- oranges_task = reactor.async do |task|
402
- task.sleep(0.01)
403
-
404
- :oranges
405
- end
406
-
407
- fruit_salad = reactor.async do |task|
408
- [apples_task.wait, oranges_task.wait]
409
- end
410
-
411
- reactor.run
412
-
413
- expect(fruit_salad.wait).to be == [:apples, :oranges]
414
- end
415
-
416
- it "will raise exceptions when checking result" do
417
- error_task = nil
418
-
419
- error_task = reactor.async do |task|
420
- raise RuntimeError, "brain not provided"
421
- end
422
-
423
- expect do
424
- error_task.wait
425
- end.to raise_exception(RuntimeError, /brain/)
426
- end
427
-
428
- it "will propagate exceptions after async operation" do
429
- error_task = nil
430
-
431
- error_task = reactor.async do |task|
432
- task.sleep(0.1)
433
-
434
- raise "boom"
435
- end
436
-
437
- innocent_task = reactor.async do |task|
438
- expect{error_task.wait}.to raise_exception RuntimeError, /boom/
439
- end
440
-
441
- expect do
442
- reactor.run
443
- end.to_not raise_exception
444
-
445
- expect(error_task).to be_finished
446
- expect(innocent_task).to be_finished
447
- end
448
- end
449
-
450
- describe '#children' do
451
- it "enumerates children in same order they are created" do
452
- tasks = 10.times.map do |i|
453
- reactor.async(annotation: "Task #{i}") {|task| task.sleep(1)}
454
- end
455
-
456
- expect(reactor.children.each.to_a).to be == tasks
457
- end
458
- end
459
-
460
- describe '#to_s' do
461
- it "should show running" do
462
- apples_task = reactor.async do |task|
463
- task.sleep(0.1)
464
- end
465
-
466
- expect(apples_task.to_s).to include "running"
467
- end
468
-
469
- it "should show complete" do
470
- apples_task = reactor.async do |task|
471
- end
472
-
473
- expect(apples_task.to_s).to include "complete"
474
- end
475
- end
476
- end