polyphony 0.45.5 → 0.47.2
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/.github/workflows/test.yml +2 -0
- data/.gitmodules +0 -0
- data/CHANGELOG.md +23 -0
- data/Gemfile.lock +1 -1
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/TODO.md +21 -22
- data/bin/test +4 -0
- data/examples/core/enumerable.rb +64 -0
- data/examples/performance/fiber_resume.rb +43 -0
- data/examples/performance/fiber_transfer.rb +13 -4
- data/examples/performance/thread-vs-fiber/compare.rb +59 -0
- data/examples/performance/thread-vs-fiber/em_server.rb +33 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +10 -21
- data/examples/performance/thread-vs-fiber/threaded_server.rb +22 -15
- data/examples/performance/thread_switch.rb +44 -0
- data/ext/liburing/liburing.h +585 -0
- data/ext/liburing/liburing/README.md +4 -0
- data/ext/liburing/liburing/barrier.h +73 -0
- data/ext/liburing/liburing/compat.h +15 -0
- data/ext/liburing/liburing/io_uring.h +343 -0
- data/ext/liburing/queue.c +333 -0
- data/ext/liburing/register.c +187 -0
- data/ext/liburing/setup.c +210 -0
- data/ext/liburing/syscall.c +54 -0
- data/ext/liburing/syscall.h +18 -0
- data/ext/polyphony/backend.h +0 -14
- data/ext/polyphony/backend_common.h +129 -0
- data/ext/polyphony/backend_io_uring.c +995 -0
- data/ext/polyphony/backend_io_uring_context.c +74 -0
- data/ext/polyphony/backend_io_uring_context.h +53 -0
- data/ext/polyphony/{libev_backend.c → backend_libev.c} +304 -294
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +31 -13
- data/ext/polyphony/fiber.c +35 -24
- data/ext/polyphony/libev.c +4 -0
- data/ext/polyphony/libev.h +8 -2
- data/ext/polyphony/liburing.c +8 -0
- data/ext/polyphony/playground.c +51 -0
- data/ext/polyphony/polyphony.c +8 -5
- data/ext/polyphony/polyphony.h +23 -19
- data/ext/polyphony/polyphony_ext.c +10 -4
- data/ext/polyphony/queue.c +100 -35
- data/ext/polyphony/thread.c +10 -10
- data/lib/polyphony/adapters/trace.rb +2 -2
- data/lib/polyphony/core/exceptions.rb +0 -4
- data/lib/polyphony/core/global_api.rb +45 -21
- data/lib/polyphony/core/resource_pool.rb +12 -1
- data/lib/polyphony/extensions/core.rb +9 -15
- data/lib/polyphony/extensions/debug.rb +13 -0
- data/lib/polyphony/extensions/fiber.rb +8 -4
- data/lib/polyphony/extensions/openssl.rb +6 -0
- data/lib/polyphony/extensions/socket.rb +73 -10
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +36 -4
- data/test/io_uring_test.rb +55 -0
- data/test/stress.rb +4 -1
- data/test/test_backend.rb +63 -6
- data/test/test_ext.rb +1 -2
- data/test/test_fiber.rb +55 -20
- data/test/test_global_api.rb +107 -35
- data/test/test_queue.rb +117 -0
- data/test/test_resource_pool.rb +21 -0
- data/test/test_socket.rb +2 -2
- data/test/test_throttler.rb +3 -6
- data/test/test_trace.rb +7 -5
- metadata +28 -3
data/test/test_ext.rb
CHANGED
@@ -183,8 +183,7 @@ class TimeoutTest < MiniTest::Test
|
|
183
183
|
end
|
184
184
|
|
185
185
|
def test_that_timeout_method_accepts_custom_error_class_and_message
|
186
|
-
|
187
|
-
spin { 3.times { |i| buffer << i; snooze } }
|
186
|
+
e = nil
|
188
187
|
begin
|
189
188
|
Timeout.timeout(0.05, MyTimeout, 'foo') { sleep 1 }
|
190
189
|
rescue Exception => e
|
data/test/test_fiber.rb
CHANGED
@@ -62,7 +62,7 @@ class FiberTest < MiniTest::Test
|
|
62
62
|
assert_equal 0, Fiber.current.children.size
|
63
63
|
end
|
64
64
|
|
65
|
-
def test_await_from_multiple_fibers_with_interruption
|
65
|
+
def test_await_from_multiple_fibers_with_interruption=
|
66
66
|
buffer = []
|
67
67
|
f1 = spin {
|
68
68
|
sleep 0.02
|
@@ -128,15 +128,16 @@ class FiberTest < MiniTest::Test
|
|
128
128
|
worker&.join
|
129
129
|
end
|
130
130
|
|
131
|
-
def
|
132
|
-
|
131
|
+
def test_backend_wakeup_mechanism
|
132
|
+
event = Polyphony::Event.new
|
133
|
+
|
133
134
|
t = Thread.new do
|
134
135
|
f = spin_loop { snooze }
|
135
136
|
sleep 0.001
|
136
|
-
|
137
|
+
event.signal(:foo)
|
137
138
|
end
|
138
139
|
|
139
|
-
result = move_on_after(1) {
|
140
|
+
result = move_on_after(1) { event.await }
|
140
141
|
|
141
142
|
assert_equal :foo, result
|
142
143
|
ensure
|
@@ -469,12 +470,14 @@ class FiberTest < MiniTest::Test
|
|
469
470
|
end
|
470
471
|
snooze # allow nested fiber to run before finishing
|
471
472
|
end
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
473
|
+
begin
|
474
|
+
suspend
|
475
|
+
rescue Exception => e
|
476
|
+
raised_error = e
|
477
|
+
ensure
|
478
|
+
assert raised_error
|
479
|
+
assert_equal 'foo', raised_error.message
|
480
|
+
end
|
478
481
|
end
|
479
482
|
|
480
483
|
def test_await_multiple_fibers
|
@@ -562,14 +565,14 @@ class FiberTest < MiniTest::Test
|
|
562
565
|
end
|
563
566
|
|
564
567
|
def test_inspect
|
565
|
-
expected = format('#<Fiber:%s (root) (running)>', Fiber.current.object_id)
|
568
|
+
expected = format('#<Fiber main:%s (root) (running)>', Fiber.current.object_id)
|
566
569
|
assert_equal expected, Fiber.current.inspect
|
567
570
|
|
568
571
|
spin_line_no = __LINE__ + 1
|
569
|
-
f = spin { :foo }
|
572
|
+
f = spin(:baz) { :foo }
|
570
573
|
|
571
574
|
expected = format(
|
572
|
-
'#<Fiber:%s %s:%d:in `test_inspect\' (runnable)>',
|
575
|
+
'#<Fiber baz:%s %s:%d:in `test_inspect\' (runnable)>',
|
573
576
|
f.object_id,
|
574
577
|
__FILE__,
|
575
578
|
spin_line_no
|
@@ -578,7 +581,7 @@ class FiberTest < MiniTest::Test
|
|
578
581
|
|
579
582
|
f.await
|
580
583
|
expected = format(
|
581
|
-
'#<Fiber:%s %s:%d:in `test_inspect\' (dead)>',
|
584
|
+
'#<Fiber baz:%s %s:%d:in `test_inspect\' (dead)>',
|
582
585
|
f.object_id,
|
583
586
|
__FILE__,
|
584
587
|
spin_line_no
|
@@ -631,11 +634,12 @@ class FiberTest < MiniTest::Test
|
|
631
634
|
def test_signal_handling_int
|
632
635
|
i, o = IO.pipe
|
633
636
|
pid = Polyphony.fork do
|
634
|
-
f = spin { sleep
|
637
|
+
f = spin { sleep 3 }
|
635
638
|
begin
|
636
639
|
i.close
|
637
640
|
f.await
|
638
641
|
rescue Exception => e
|
642
|
+
trace e
|
639
643
|
o << e.class.name
|
640
644
|
o.close
|
641
645
|
end
|
@@ -653,7 +657,7 @@ class FiberTest < MiniTest::Test
|
|
653
657
|
def test_signal_handling_term
|
654
658
|
i, o = IO.pipe
|
655
659
|
pid = Polyphony.fork do
|
656
|
-
f = spin { sleep
|
660
|
+
f = spin { sleep 3 }
|
657
661
|
begin
|
658
662
|
i.close
|
659
663
|
f.await
|
@@ -662,7 +666,7 @@ class FiberTest < MiniTest::Test
|
|
662
666
|
o.close
|
663
667
|
end
|
664
668
|
end
|
665
|
-
sleep 0.
|
669
|
+
sleep 0.1
|
666
670
|
f = spin { Thread.current.backend.waitpid(pid) }
|
667
671
|
o.close
|
668
672
|
Process.kill('TERM', pid)
|
@@ -677,7 +681,7 @@ class FiberTest < MiniTest::Test
|
|
677
681
|
pid = Polyphony.fork do
|
678
682
|
i.close
|
679
683
|
spin do
|
680
|
-
sleep
|
684
|
+
sleep 3
|
681
685
|
rescue Exception => e
|
682
686
|
o << e.class.to_s
|
683
687
|
o.close
|
@@ -687,7 +691,7 @@ class FiberTest < MiniTest::Test
|
|
687
691
|
end
|
688
692
|
o.close
|
689
693
|
spin do
|
690
|
-
sleep 0.
|
694
|
+
sleep 0.1
|
691
695
|
Process.kill('TERM', pid)
|
692
696
|
end
|
693
697
|
Thread.current.backend.waitpid(pid)
|
@@ -703,6 +707,7 @@ class FiberTest < MiniTest::Test
|
|
703
707
|
assert_nil f.thread
|
704
708
|
snooze
|
705
709
|
f.setup_raw
|
710
|
+
|
706
711
|
assert_equal Thread.current, f.thread
|
707
712
|
assert_nil f.parent
|
708
713
|
|
@@ -711,6 +716,7 @@ class FiberTest < MiniTest::Test
|
|
711
716
|
f << 'bar'
|
712
717
|
snooze
|
713
718
|
assert_equal ['bar'], buffer
|
719
|
+
snooze
|
714
720
|
end
|
715
721
|
end
|
716
722
|
|
@@ -732,6 +738,35 @@ class MailboxTest < MiniTest::Test
|
|
732
738
|
f&.stop
|
733
739
|
end
|
734
740
|
|
741
|
+
def test_capped_fiber_mailbox
|
742
|
+
buffer = []
|
743
|
+
a = spin_loop do
|
744
|
+
3.times { snooze }
|
745
|
+
buffer << [:receive, receive]
|
746
|
+
end
|
747
|
+
a.mailbox.cap(1)
|
748
|
+
|
749
|
+
b = spin do
|
750
|
+
(1..3).each do |i|
|
751
|
+
a << i
|
752
|
+
buffer << [:send, i]
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
(1..10).each do |i|
|
757
|
+
snooze
|
758
|
+
buffer << [:snooze, i]
|
759
|
+
end
|
760
|
+
|
761
|
+
b.join
|
762
|
+
|
763
|
+
assert_equal [
|
764
|
+
[:snooze, 1], [:send, 1], [:snooze, 2], [:snooze, 3], [:snooze, 4],
|
765
|
+
[:receive, 1], [:snooze, 5], [:send, 2], [:snooze, 6], [:snooze, 7],
|
766
|
+
[:snooze, 8], [:receive, 2], [:snooze, 9], [:send, 3], [:snooze, 10]
|
767
|
+
], buffer
|
768
|
+
end
|
769
|
+
|
735
770
|
def test_that_multiple_messages_sent_at_once_arrive_in_order
|
736
771
|
msgs = []
|
737
772
|
f = spin { loop { msgs << receive } }
|
data/test/test_global_api.rb
CHANGED
@@ -72,28 +72,32 @@ class ExceptionTest < MiniTest::Test
|
|
72
72
|
rescue Exception => e
|
73
73
|
frames << 3
|
74
74
|
raise e
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
end
|
76
|
+
begin
|
77
|
+
5.times { snooze }
|
78
|
+
rescue Exception => e
|
79
|
+
error = e
|
80
|
+
ensure
|
81
|
+
assert_kind_of RuntimeError, error
|
82
|
+
assert_equal [2, 3], frames
|
83
|
+
end
|
82
84
|
end
|
83
85
|
|
84
86
|
def test_cross_fiber_backtrace_with_dead_calling_fiber
|
85
87
|
error = nil
|
86
|
-
|
88
|
+
begin
|
87
89
|
spin do
|
88
90
|
spin do
|
89
|
-
|
91
|
+
spin do
|
92
|
+
raise 'foo'
|
93
|
+
end.await
|
90
94
|
end.await
|
91
95
|
end.await
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
96
|
+
rescue Exception => e
|
97
|
+
error = e
|
98
|
+
ensure
|
99
|
+
assert_kind_of RuntimeError, error
|
100
|
+
end
|
97
101
|
end
|
98
102
|
end
|
99
103
|
|
@@ -133,8 +137,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
133
137
|
t1 = Time.now
|
134
138
|
|
135
139
|
assert_nil v
|
136
|
-
|
137
|
-
assert t1 - t0 < 0.02
|
140
|
+
assert_in_range 0.014..0.02, t1 - t0
|
138
141
|
end
|
139
142
|
|
140
143
|
def test_move_on_after_without_block
|
@@ -147,6 +150,28 @@ class MoveOnAfterTest < MiniTest::Test
|
|
147
150
|
assert t1 - t0 < 0.1
|
148
151
|
assert_equal 'foo', v
|
149
152
|
end
|
153
|
+
|
154
|
+
def test_nested_move_on_after
|
155
|
+
t0 = Time.now
|
156
|
+
o = move_on_after(0.01, with_value: 1) do
|
157
|
+
move_on_after(0.02, with_value: 2) do
|
158
|
+
sleep 1
|
159
|
+
end
|
160
|
+
end
|
161
|
+
t1 = Time.now
|
162
|
+
assert_equal 1, o
|
163
|
+
assert_in_range 0.008..0.013, t1 - t0
|
164
|
+
|
165
|
+
t0 = Time.now
|
166
|
+
o = move_on_after(0.02, with_value: 1) do
|
167
|
+
move_on_after(0.01, with_value: 2) do
|
168
|
+
sleep 1
|
169
|
+
end
|
170
|
+
end
|
171
|
+
t1 = Time.now
|
172
|
+
assert_equal 2, o
|
173
|
+
assert_in_range 0.008..0.013, t1 - t0
|
174
|
+
end
|
150
175
|
end
|
151
176
|
|
152
177
|
class CancelAfterTest < MiniTest::Test
|
@@ -185,8 +210,7 @@ class CancelAfterTest < MiniTest::Test
|
|
185
210
|
sleep 0.007
|
186
211
|
end
|
187
212
|
t1 = Time.now
|
188
|
-
|
189
|
-
assert t1 - t0 < 0.02
|
213
|
+
assert_in_range 0.014..0.02, t1 - t0
|
190
214
|
end
|
191
215
|
|
192
216
|
class CustomException < Exception
|
@@ -200,6 +224,19 @@ class CancelAfterTest < MiniTest::Test
|
|
200
224
|
end
|
201
225
|
end
|
202
226
|
|
227
|
+
begin
|
228
|
+
err = nil
|
229
|
+
cancel_after(0.01, with_exception: [CustomException, 'custom message']) do
|
230
|
+
sleep 1
|
231
|
+
:foo
|
232
|
+
end
|
233
|
+
rescue Exception => err
|
234
|
+
ensure
|
235
|
+
assert_kind_of CustomException, err
|
236
|
+
assert_equal 'custom message', err.message
|
237
|
+
end
|
238
|
+
|
239
|
+
|
203
240
|
begin
|
204
241
|
e = nil
|
205
242
|
cancel_after(0.01, with_exception: 'foo') do
|
@@ -255,12 +292,49 @@ class SpinLoopTest < MiniTest::Test
|
|
255
292
|
buffer = []
|
256
293
|
counter = 0
|
257
294
|
t0 = Time.now
|
258
|
-
f = spin_loop(rate:
|
259
|
-
sleep 0.
|
295
|
+
f = spin_loop(rate: 100) { buffer << (counter += 1) }
|
296
|
+
sleep 0.02
|
260
297
|
f.stop
|
261
|
-
|
262
|
-
|
263
|
-
|
298
|
+
assert_in_range 1..3, counter
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
class SpinScopeTest < MiniTest::Test
|
303
|
+
def test_spin_scope
|
304
|
+
queue = Queue.new
|
305
|
+
buffer = {}
|
306
|
+
spin do
|
307
|
+
queue << 1
|
308
|
+
snooze
|
309
|
+
queue << 2
|
310
|
+
end
|
311
|
+
f = nil
|
312
|
+
result = spin_scope do
|
313
|
+
f = Fiber.current
|
314
|
+
spin { buffer[:a] = queue.shift }
|
315
|
+
spin { buffer[:b] = queue.shift }
|
316
|
+
:foo
|
317
|
+
end
|
318
|
+
assert_equal :foo, result
|
319
|
+
assert_kind_of Fiber, f
|
320
|
+
assert_equal :dead, f.state
|
321
|
+
assert_equal ({a: 1, b: 2}), buffer
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_spin_scope_with_exception
|
325
|
+
queue = Queue.new
|
326
|
+
buffer = []
|
327
|
+
spin do
|
328
|
+
spin_scope do
|
329
|
+
spin { buffer << queue.shift }
|
330
|
+
spin { raise 'foobar' }
|
331
|
+
end
|
332
|
+
rescue => e
|
333
|
+
buffer << e.message
|
334
|
+
end
|
335
|
+
6.times { snooze }
|
336
|
+
assert_equal 0, Fiber.current.children.size
|
337
|
+
assert_equal ['foobar'], buffer
|
264
338
|
end
|
265
339
|
end
|
266
340
|
|
@@ -270,22 +344,22 @@ class ThrottledLoopTest < MiniTest::Test
|
|
270
344
|
counter = 0
|
271
345
|
t0 = Time.now
|
272
346
|
f = spin do
|
273
|
-
throttled_loop(
|
347
|
+
throttled_loop(100) { buffer << (counter += 1) }
|
274
348
|
end
|
275
|
-
sleep 0.
|
276
|
-
|
277
|
-
elapsed = Time.now - t0
|
278
|
-
expected = (elapsed * 10).to_i
|
279
|
-
assert counter >= expected - 1 && counter <= expected + 1
|
349
|
+
sleep 0.03
|
350
|
+
assert_in_range 2..4, counter
|
280
351
|
end
|
281
352
|
|
282
353
|
def test_throttled_loop_with_count
|
283
354
|
buffer = []
|
284
355
|
counter = 0
|
356
|
+
t0 = Time.now
|
285
357
|
f = spin do
|
286
358
|
throttled_loop(50, count: 5) { buffer << (counter += 1) }
|
287
359
|
end
|
288
360
|
f.await
|
361
|
+
t1 = Time.now
|
362
|
+
assert_in_range 0.075..0.15, t1 - t0
|
289
363
|
assert_equal [1, 2, 3, 4, 5], buffer
|
290
364
|
end
|
291
365
|
end
|
@@ -304,13 +378,11 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
304
378
|
buffer = []
|
305
379
|
t0 = Time.now
|
306
380
|
f = spin do
|
307
|
-
every(0.
|
381
|
+
every(0.01) { buffer << 1 }
|
308
382
|
end
|
309
|
-
sleep 0.
|
383
|
+
sleep 0.05
|
310
384
|
f.stop
|
311
|
-
|
312
|
-
expected = (elapsed / 0.1).to_i
|
313
|
-
assert buffer.size >= expected - 2 && buffer.size <= expected + 2
|
385
|
+
assert_in_range 4..6, buffer.size
|
314
386
|
end
|
315
387
|
|
316
388
|
def test_sleep
|
@@ -378,4 +450,4 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
378
450
|
|
379
451
|
assert_equal [0, 1, 2], values
|
380
452
|
end
|
381
|
-
end
|
453
|
+
end
|
data/test/test_queue.rb
CHANGED
@@ -129,4 +129,121 @@ class QueueTest < MiniTest::Test
|
|
129
129
|
|
130
130
|
assert_equal 0, @queue.size
|
131
131
|
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class CappedQueueTest < MiniTest::Test
|
135
|
+
def setup
|
136
|
+
super
|
137
|
+
@queue = Polyphony::Queue.new
|
138
|
+
@queue.cap(3)
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_capped?
|
142
|
+
q = Polyphony::Queue.new
|
143
|
+
assert_nil q.capped?
|
144
|
+
|
145
|
+
q.cap(3)
|
146
|
+
assert_equal 3, q.capped?
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_initalize_with_cap
|
150
|
+
q = Polyphony::Queue.new(42)
|
151
|
+
assert_equal 42, q.capped?
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_capped_push
|
155
|
+
buffer = []
|
156
|
+
a = spin do
|
157
|
+
(1..5).each do |i|
|
158
|
+
@queue.push(i)
|
159
|
+
buffer << :"p#{i}"
|
160
|
+
end
|
161
|
+
@queue.push :stop
|
162
|
+
end
|
163
|
+
|
164
|
+
snooze
|
165
|
+
|
166
|
+
b = spin_loop do
|
167
|
+
i = @queue.shift
|
168
|
+
raise Polyphony::Terminate if i == :stop
|
169
|
+
buffer << :"s#{i}"
|
170
|
+
end
|
171
|
+
|
172
|
+
Fiber.join(a, b)
|
173
|
+
assert_equal [:p1, :p2, :s1, :p3, :s2, :p4, :s3, :p5, :s4, :s5], buffer
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_capped_multi_push
|
177
|
+
buffer = []
|
178
|
+
a = spin(:a) do
|
179
|
+
(1..3).each do |i|
|
180
|
+
@queue.push(i)
|
181
|
+
buffer << :"p#{i}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
buffer = []
|
186
|
+
b = spin(:b) do
|
187
|
+
(4..6).each do |i|
|
188
|
+
@queue.push(i)
|
189
|
+
buffer << :"p#{i}"
|
190
|
+
end
|
191
|
+
@queue.push :stop
|
192
|
+
end
|
193
|
+
|
194
|
+
c = spin_loop do
|
195
|
+
i = @queue.shift
|
196
|
+
raise Polyphony::Terminate if i == :stop
|
197
|
+
buffer << :"s#{i}"
|
198
|
+
snooze
|
199
|
+
end
|
200
|
+
|
201
|
+
Fiber.join(a, b, c)
|
202
|
+
assert_equal [:p1, :p4, :s1, :p5, :p2, :s4, :p3, :s5, :p6, :s2, :s3, :s6], buffer
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_capped_clear
|
206
|
+
buffer = []
|
207
|
+
a = spin(:a) do
|
208
|
+
(1..5).each do |i|
|
209
|
+
@queue.push(i)
|
210
|
+
buffer << i
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
snooze while buffer.size < 3
|
215
|
+
@queue.clear
|
216
|
+
buffer << :clear
|
217
|
+
|
218
|
+
a.join
|
219
|
+
assert_equal [1, 2, 3, :clear, 4, 5], buffer
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_capped_delete
|
223
|
+
buffer = []
|
224
|
+
a = spin(:a) do
|
225
|
+
(1..5).each do |i|
|
226
|
+
@queue.push(i)
|
227
|
+
buffer << i
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
i = 0
|
232
|
+
spin_loop do
|
233
|
+
i += 1
|
234
|
+
snooze
|
235
|
+
end
|
236
|
+
|
237
|
+
5.times { snooze }
|
238
|
+
assert_equal 5, i
|
239
|
+
@queue.delete 1
|
240
|
+
buffer << :"d#{i}"
|
241
|
+
3.times { snooze }
|
242
|
+
assert_equal 8, i
|
243
|
+
@queue.delete 2
|
244
|
+
buffer << :"d#{i}"
|
245
|
+
|
246
|
+
a.join
|
247
|
+
assert_equal [1, 2, 3, :d5, 4, :d8, 5], buffer
|
248
|
+
end
|
132
249
|
end
|