polyphony 0.29 → 0.30

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile.lock +1 -1
  5. data/TODO.md +15 -10
  6. data/docs/getting-started/tutorial.md +3 -3
  7. data/docs/index.md +2 -3
  8. data/docs/{technical-overview → main-concepts}/concurrency.md +62 -15
  9. data/docs/{technical-overview → main-concepts}/design-principles.md +21 -8
  10. data/docs/{technical-overview → main-concepts}/exception-handling.md +80 -38
  11. data/docs/{technical-overview → main-concepts}/extending.md +4 -3
  12. data/docs/{technical-overview → main-concepts}/fiber-scheduling.md +3 -3
  13. data/docs/{technical-overview.md → main-concepts.md} +2 -2
  14. data/examples/core/xx-at_exit.rb +29 -0
  15. data/examples/core/xx-fork-terminate.rb +27 -0
  16. data/examples/core/xx-pingpong.rb +18 -0
  17. data/examples/core/xx-stop.rb +20 -0
  18. data/ext/gyro/async.c +1 -1
  19. data/ext/gyro/extconf.rb +0 -3
  20. data/ext/gyro/gyro.c +7 -8
  21. data/ext/gyro/gyro.h +2 -0
  22. data/ext/gyro/queue.c +6 -6
  23. data/ext/gyro/selector.c +32 -1
  24. data/ext/gyro/thread.c +55 -9
  25. data/ext/gyro/timer.c +1 -0
  26. data/lib/polyphony/core/exceptions.rb +4 -1
  27. data/lib/polyphony/core/global_api.rb +1 -6
  28. data/lib/polyphony/core/thread_pool.rb +3 -3
  29. data/lib/polyphony/extensions/core.rb +7 -1
  30. data/lib/polyphony/extensions/fiber.rb +159 -72
  31. data/lib/polyphony/extensions/io.rb +2 -4
  32. data/lib/polyphony/extensions/openssl.rb +0 -17
  33. data/lib/polyphony/extensions/thread.rb +46 -22
  34. data/lib/polyphony/version.rb +1 -1
  35. data/lib/polyphony.rb +20 -18
  36. data/test/coverage.rb +1 -1
  37. data/test/helper.rb +7 -3
  38. data/test/test_fiber.rb +285 -72
  39. data/test/test_global_api.rb +7 -52
  40. data/test/test_io.rb +8 -0
  41. data/test/test_signal.rb +1 -0
  42. data/test/test_thread.rb +76 -56
  43. data/test/test_thread_pool.rb +27 -5
  44. data/test/test_throttler.rb +1 -0
  45. metadata +12 -12
  46. data/lib/polyphony/core/supervisor.rb +0 -114
  47. data/lib/polyphony/line_reader.rb +0 -82
  48. data/test/test_gyro.rb +0 -25
  49. data/test/test_supervisor.rb +0 -180
data/test/test_fiber.rb CHANGED
@@ -5,7 +5,7 @@ require_relative 'helper'
5
5
  class FiberTest < MiniTest::Test
6
6
  def test_spin_initial_state
7
7
  result = nil
8
- f = Fiber.spin { result = 42 }
8
+ f = Fiber.current.spin { result = 42 }
9
9
  assert_nil result
10
10
  f.await
11
11
  assert_equal 42, result
@@ -15,7 +15,7 @@ class FiberTest < MiniTest::Test
15
15
 
16
16
  def test_await
17
17
  result = nil
18
- f = Fiber.spin do
18
+ f = Fiber.current.spin do
19
19
  snooze
20
20
  result = 42
21
21
  end
@@ -25,6 +25,49 @@ class FiberTest < MiniTest::Test
25
25
  f&.stop
26
26
  end
27
27
 
28
+ def test_await_from_multiple_fibers
29
+ buffer = []
30
+ f1 = spin {
31
+ sleep 0.02
32
+ buffer << :foo
33
+ }
34
+ f2 = spin {
35
+ f1.await
36
+ buffer << :bar
37
+ }
38
+ f3 = spin {
39
+ f1.await
40
+ buffer << :baz
41
+ }
42
+ Fiber.await(f2, f3)
43
+ assert_equal [:foo, :bar, :baz], buffer
44
+ assert_equal 0, Fiber.current.children.size
45
+ end
46
+
47
+ def test_await_from_multiple_fibers_with_interruption
48
+ buffer = []
49
+ f1 = spin {
50
+ sleep 0.02
51
+ buffer << :foo
52
+ }
53
+ f2 = spin {
54
+ f1.await
55
+ buffer << :bar
56
+ }
57
+ f3 = spin {
58
+ f1.await
59
+ buffer << :baz
60
+ }
61
+ snooze
62
+ f2.stop
63
+ f3.stop
64
+ snooze
65
+ f1.stop
66
+
67
+ snooze
68
+ assert_equal [], Fiber.current.children
69
+ end
70
+
28
71
  def test_schedule
29
72
  values = []
30
73
  fibers = (0..2).map { |i| spin { suspend; values << i } }
@@ -74,7 +117,7 @@ class FiberTest < MiniTest::Test
74
117
  async.signal!(:foo)
75
118
  end
76
119
 
77
- result = move_on_after(0.05) { async.await }
120
+ result = move_on_after(1) { async.await }
78
121
 
79
122
  assert_equal :foo, result
80
123
  ensure
@@ -86,12 +129,12 @@ class FiberTest < MiniTest::Test
86
129
  Fiber.current.tag = :foo
87
130
  assert_equal :foo, Fiber.current.tag
88
131
 
89
- f = Fiber.spin(:bar) { }
132
+ f = Fiber.current.spin(:bar) { }
90
133
  assert_equal :bar, f.tag
91
134
  end
92
135
 
93
136
  def test_await_return_value
94
- f = Fiber.spin { %i[foo bar] }
137
+ f = Fiber.current.spin { %i[foo bar] }
95
138
  assert_equal %i[foo bar], f.await
96
139
  ensure
97
140
  f&.stop
@@ -99,7 +142,7 @@ class FiberTest < MiniTest::Test
99
142
 
100
143
  def test_await_with_error
101
144
  result = nil
102
- f = Fiber.spin { raise 'foo' }
145
+ f = Fiber.current.spin { raise 'foo' }
103
146
  begin
104
147
  result = f.await
105
148
  rescue Exception => e
@@ -114,7 +157,7 @@ class FiberTest < MiniTest::Test
114
157
  def test_raise
115
158
  result = []
116
159
  error = nil
117
- f = Fiber.spin do
160
+ f = Fiber.current.spin do
118
161
  result << 1
119
162
  2.times { snooze }
120
163
  result << 2
@@ -139,7 +182,7 @@ class FiberTest < MiniTest::Test
139
182
  def test_raise_with_error_class
140
183
  result = []
141
184
  error = nil
142
- f = Fiber.spin do
185
+ f = Fiber.current.spin do
143
186
  result << 1
144
187
  2.times { snooze }
145
188
  result << 2
@@ -161,7 +204,7 @@ class FiberTest < MiniTest::Test
161
204
  def test_raise_with_error_class_and_message
162
205
  result = []
163
206
  error = nil
164
- f = Fiber.spin do
207
+ f = Fiber.current.spin do
165
208
  result << 1
166
209
  2.times { snooze }
167
210
  result << 2
@@ -184,7 +227,7 @@ class FiberTest < MiniTest::Test
184
227
  def test_raise_with_message
185
228
  result = []
186
229
  error = nil
187
- f = Fiber.spin do
230
+ f = Fiber.current.spin do
188
231
  result << 1
189
232
  2.times { snooze }
190
233
  result << 2
@@ -207,7 +250,7 @@ class FiberTest < MiniTest::Test
207
250
  def test_raise_with_exception
208
251
  result = []
209
252
  error = nil
210
- f = Fiber.spin do
253
+ f = Fiber.current.spin do
211
254
  result << 1
212
255
  2.times { snooze }
213
256
  result << 2
@@ -230,7 +273,7 @@ class FiberTest < MiniTest::Test
230
273
  def test_cancel
231
274
  result = []
232
275
  error = nil
233
- f = Fiber.spin do
276
+ f = Fiber.current.spin do
234
277
  result << 1
235
278
  2.times { snooze }
236
279
  result << 2
@@ -252,7 +295,7 @@ class FiberTest < MiniTest::Test
252
295
  def test_interrupt
253
296
  # that is, stopped without exception
254
297
  result = []
255
- f = Fiber.spin do
298
+ f = Fiber.current.spin do
256
299
  result << 1
257
300
  2.times { snooze }
258
301
  result << 2
@@ -267,10 +310,39 @@ class FiberTest < MiniTest::Test
267
310
  f&.stop
268
311
  end
269
312
 
313
+ def test_terminate
314
+ buffer = []
315
+ f = spin do
316
+ buffer << :foo
317
+ sleep 1
318
+ buffer << :bar
319
+ rescue Polyphony::Terminate
320
+ buffer << :terminate
321
+ end
322
+ snooze
323
+ f.terminate
324
+ snooze
325
+ assert_equal [:foo, :terminate], buffer
326
+ end
327
+
328
+ def test_interrupt_timer
329
+ result = []
330
+ f = Fiber.current.spin do
331
+ result << :start
332
+ t = Gyro::Timer.new(1, 0)
333
+ result << t.await
334
+ end
335
+ snooze
336
+ f.interrupt
337
+ f.join
338
+
339
+ assert_equal [:start], result
340
+ end
341
+
270
342
  def test_stop
271
343
  # that is, stopped without exception
272
344
  result = []
273
- f = Fiber.spin do
345
+ f = Fiber.current.spin do
274
346
  result << 1
275
347
  2.times { snooze }
276
348
  result << 2
@@ -287,7 +359,7 @@ class FiberTest < MiniTest::Test
287
359
 
288
360
  def test_interrupt_before_start
289
361
  result = []
290
- f = Fiber.spin do
362
+ f = Fiber.current.spin do
291
363
  result << 1
292
364
  end
293
365
  f.interrupt(42)
@@ -336,12 +408,21 @@ class FiberTest < MiniTest::Test
336
408
  snooze while counter < 3
337
409
  assert_equal :waiting, f.state
338
410
  f.stop
411
+ snooze
339
412
  assert_equal :dead, f.state
340
413
  ensure
341
414
  f&.stop
342
415
  end
343
416
 
344
- def test_exception_bubbling
417
+ def test_main?
418
+ f = spin {
419
+ sleep
420
+ }
421
+ assert_nil f.main?
422
+ assert_equal true, Fiber.current.main?
423
+ end
424
+
425
+ def test_exception_propagation
345
426
  # error is propagated to calling fiber
346
427
  raised_error = nil
347
428
  spin do
@@ -358,22 +439,6 @@ class FiberTest < MiniTest::Test
358
439
  assert_equal 'foo', raised_error.message
359
440
  end
360
441
 
361
- def test_exception_bubling_for_orphan_fiber
362
- raised_error = nil
363
- spin do
364
- spin do
365
- snooze
366
- raise 'bar'
367
- end
368
- end
369
- suspend
370
- rescue Exception => e
371
- raised_error = e
372
- ensure
373
- assert raised_error
374
- assert_equal 'bar', raised_error.message
375
- end
376
-
377
442
  def test_await_multiple_fibers
378
443
  f1 = spin { sleep 0.01; :foo }
379
444
  f2 = spin { sleep 0.01; :bar }
@@ -399,7 +464,7 @@ class FiberTest < MiniTest::Test
399
464
  f2 = spin { sleep 0.03; buffer << :bar; :bar }
400
465
  f3 = spin { sleep 0.05; buffer << :baz; :baz }
401
466
 
402
- result, selected = Fiber.select(f1, f2, f3)
467
+ selected, result = Fiber.select(f1, f2, f3)
403
468
  assert_equal :foo, result
404
469
  assert_equal f1, selected
405
470
  assert_equal [:foo], buffer
@@ -446,18 +511,16 @@ class FiberTest < MiniTest::Test
446
511
  assert !f.running?
447
512
  end
448
513
 
449
- def test_list_and_count
450
- assert_equal 1, Fiber.count
451
- assert_equal [Fiber.current], Fiber.list
514
+ def test_children
515
+ assert_equal [], Fiber.current.children
452
516
 
453
517
  f = spin { sleep 1 }
454
518
  snooze
455
- assert_equal 2, Fiber.count
456
- assert_equal f, Fiber.list.last
519
+ assert_equal [f], Fiber.current.children
457
520
 
458
521
  f.stop
459
522
  snooze
460
- assert_equal 1, Fiber.count
523
+ assert_equal [], Fiber.current.children
461
524
  end
462
525
 
463
526
  def test_inspect
@@ -486,60 +549,109 @@ class FiberTest < MiniTest::Test
486
549
  end
487
550
 
488
551
  def test_system_exit_in_fiber
489
- parent_error = nil
490
- main_fiber_error = nil
491
- f2 = nil
492
- f1 = spin do
493
- f2 = spin { raise SystemExit }
494
- suspend
495
- rescue Exception => parent_error
552
+ error = nil
553
+ spin do
554
+ spin { raise SystemExit }.await
496
555
  end
497
556
 
498
557
  begin
499
558
  suspend
500
- rescue Exception => main_fiber_error
559
+ rescue Exception => error
501
560
  end
502
561
 
503
- assert_nil parent_error
504
- assert_kind_of SystemExit, main_fiber_error
562
+ assert_kind_of SystemExit, error
505
563
  end
506
564
 
507
565
  def test_interrupt_in_fiber
508
- parent_error = nil
509
- main_fiber_error = nil
510
- f2 = nil
511
- f1 = spin do
512
- f2 = spin { raise Interrupt }
513
- suspend
514
- rescue Exception => parent_error
566
+ error = nil
567
+ spin do
568
+ spin { raise Interrupt }.await
515
569
  end
516
570
 
517
571
  begin
518
572
  suspend
519
- rescue Exception => main_fiber_error
573
+ rescue Exception => error
520
574
  end
521
575
 
522
- assert_nil parent_error
523
- assert_kind_of Interrupt, main_fiber_error
576
+ assert_kind_of Interrupt, error
524
577
  end
525
578
 
526
579
  def test_signal_exception_in_fiber
527
- parent_error = nil
528
- main_fiber_error = nil
529
- f2 = nil
530
- f1 = spin do
531
- f2 = spin { raise SignalException.new('HUP') }
532
- suspend
533
- rescue Exception => parent_error
580
+ error = nil
581
+ spin do
582
+ spin { raise SignalException.new('HUP') }.await
534
583
  end
535
584
 
536
585
  begin
537
586
  suspend
538
- rescue Exception => main_fiber_error
587
+ rescue Exception => error
539
588
  end
540
589
 
541
- assert_nil parent_error
542
- assert_kind_of SignalException, main_fiber_error
590
+ assert_kind_of SignalException, error
591
+ end
592
+
593
+ def test_signal_handling_int
594
+ i, o = IO.pipe
595
+ pid = Polyphony.fork do
596
+ f = spin { sleep 100 }
597
+ begin
598
+ i.close
599
+ f.await
600
+ rescue Exception => e
601
+ o << e.class.name
602
+ o.close
603
+ end
604
+ end
605
+ sleep 0.1
606
+ f = spin { Gyro::Child.new(pid).await }
607
+ o.close
608
+ Process.kill('INT', pid)
609
+ f.await
610
+ klass = i.read
611
+ o.close
612
+ assert_equal 'Interrupt', klass
613
+ end
614
+
615
+ def test_signal_handling_term
616
+ i, o = IO.pipe
617
+ pid = Polyphony.fork do
618
+ f = spin { sleep 100 }
619
+ begin
620
+ i.close
621
+ f.await
622
+ rescue Exception => e
623
+ o << e.class.name
624
+ o.close
625
+ end
626
+ end
627
+ sleep 0.1
628
+ f = spin { Gyro::Child.new(pid).await }
629
+ o.close
630
+ Process.kill('TERM', pid)
631
+ f.await
632
+ klass = i.read
633
+ o.close
634
+ assert_equal 'SystemExit', klass
635
+ end
636
+
637
+ def test_main_fiber_child_termination_after_fork
638
+ i, o = IO.pipe
639
+ pid = Polyphony.fork do
640
+ i.close
641
+ f = spin do
642
+ sleep 100
643
+ rescue Exception => e
644
+ o << e.class.to_s
645
+ o.close
646
+ end
647
+ snooze
648
+ ensure
649
+ end
650
+ o.close
651
+ Gyro::Child.new(pid).await
652
+ klass = i.read
653
+ i.close
654
+ assert_equal 'Polyphony::Terminate', klass
543
655
  end
544
656
  end
545
657
 
@@ -552,7 +664,7 @@ class MailboxTest < MiniTest::Test
552
664
 
553
665
  3.times do |i|
554
666
  f << i
555
- snooze
667
+ sleep 0
556
668
  end
557
669
 
558
670
  assert_equal [0, 1, 2], msgs
@@ -568,7 +680,7 @@ class MailboxTest < MiniTest::Test
568
680
 
569
681
  3.times { |i| f << i }
570
682
 
571
- snooze
683
+ sleep 0
572
684
 
573
685
  assert_equal [0, 1, 2], msgs
574
686
  ensure
@@ -588,10 +700,10 @@ class MailboxTest < MiniTest::Test
588
700
  end
589
701
 
590
702
  def test_cross_thread_send_receive
591
- skip "There's currently a race condition in cross-thread send/receive. We're going to rewrite it in C"
592
703
  ping_receive_buffer = []
593
704
  pong_receive_buffer = []
594
705
  pong = Thread.new do
706
+ sleep 0.05
595
707
  loop do
596
708
  peer, data = receive
597
709
  pong_receive_buffer << data
@@ -600,6 +712,7 @@ class MailboxTest < MiniTest::Test
600
712
  end
601
713
 
602
714
  ping = Thread.new do
715
+ sleep 0.05
603
716
  3.times do
604
717
  pong << [Fiber.current, 'ping']
605
718
  data = receive
@@ -613,4 +726,104 @@ class MailboxTest < MiniTest::Test
613
726
  assert_equal %w{pong pong pong}, ping_receive_buffer
614
727
  assert_equal %w{ping ping ping}, pong_receive_buffer
615
728
  end
729
+
730
+ def test_message_queueing
731
+ messages = []
732
+ f = spin do
733
+ loop {
734
+ msg = receive
735
+ break if msg == 'stop'
736
+
737
+ messages << msg
738
+ }
739
+ end
740
+
741
+ 100.times { f << 'foo' }
742
+ f << 'stop'
743
+
744
+ f.await
745
+ assert_equal ['foo'] * 100, messages
746
+ end
616
747
  end
748
+
749
+ class FiberControlTest < MiniTest::Test
750
+ def test_await_multiple
751
+ f1 = spin {
752
+ snooze
753
+ :foo
754
+ }
755
+ f2 = spin {
756
+ snooze
757
+ :bar
758
+ }
759
+ result = Fiber.await(f1, f2)
760
+ assert_equal [:foo, :bar], result
761
+ end
762
+
763
+ def test_await_multiple_with_raised_error
764
+ f1 = spin {
765
+ snooze
766
+ raise 'foo'
767
+ }
768
+ f2 = spin {
769
+ snooze
770
+ :bar
771
+ }
772
+ f3 = spin {
773
+ sleep 3
774
+ }
775
+ error = nil
776
+ begin
777
+ Fiber.await(f1, f2, f3)
778
+ rescue => error
779
+ end
780
+ assert_kind_of RuntimeError, error
781
+ assert_equal 'foo', error.message
782
+
783
+ assert_equal :dead, f1.state
784
+ assert_equal :dead, f2.state
785
+ assert_equal :dead, f3.state
786
+ end
787
+
788
+ def test_await_multiple_with_interruption
789
+ f1 = spin { sleep 0.01; :foo }
790
+ f2 = spin { sleep 1; :bar }
791
+ spin { snooze; f2.interrupt(:baz) }
792
+ result = Fiber.await(f1, f2)
793
+ assert_equal [:foo, :baz], result
794
+ end
795
+
796
+ def test_select
797
+ buffer = []
798
+ f1 = spin { snooze; buffer << :foo; :foo }
799
+ f2 = spin { :bar }
800
+ result = Fiber.select(f1, f2)
801
+ assert_equal [f2, :bar], result
802
+ assert_equal [:foo], buffer
803
+ assert_equal :dead, f1.state
804
+ end
805
+
806
+ def test_select_with_raised_error
807
+ f1 = spin { snooze; raise 'foo' }
808
+ f2 = spin { sleep 3 }
809
+
810
+ result = nil
811
+ begin
812
+ result = Fiber.select(f1, f2)
813
+ rescue => result
814
+ end
815
+
816
+ assert_kind_of RuntimeError, result
817
+ assert_equal 'foo', result.message
818
+ assert_equal :dead, f1.state
819
+ assert_equal :dead, f2.state
820
+ end
821
+
822
+ def test_select_with_interruption
823
+ f1 = spin { sleep 0.01; :foo }
824
+ f2 = spin { sleep 1; :bar }
825
+ spin { snooze; f2.interrupt(:baz) }
826
+ result = Fiber.select(f1, f2)
827
+ assert_equal [f2, :baz], result
828
+ end
829
+ end
@@ -15,7 +15,7 @@ class SpinTest < MiniTest::Test
15
15
 
16
16
  def test_that_spin_accepts_fiber_argument
17
17
  result = nil
18
- fiber = Fiber.spin { result = 42 }
18
+ fiber = Fiber.current.spin { result = 42 }
19
19
 
20
20
  assert_nil result
21
21
  suspend
@@ -128,51 +128,6 @@ class CancelScopeTest < Minitest::Test
128
128
  # end
129
129
  end
130
130
 
131
- class SupervisorTest < MiniTest::Test
132
- def sleep_and_set(ctx, idx)
133
- proc do
134
- sleep(0.001 * idx)
135
- ctx[idx] = true
136
- end
137
- end
138
-
139
- def parallel_sleep(ctx)
140
- supervise do |s|
141
- (1..3).each { |idx| s.spin(&sleep_and_set(ctx, idx)) }
142
- end
143
- end
144
-
145
- def test_that_supervisor_waits_for_all_nested_fibers_to_complete
146
- ctx = {}
147
- spin do
148
- parallel_sleep(ctx)
149
- end
150
- suspend
151
- assert ctx[1]
152
- assert ctx[2]
153
- assert ctx[3]
154
- end
155
-
156
- def test_that_supervisor_can_add_fibers_after_having_started
157
- result = []
158
- spin do
159
- supervisor = Polyphony::Supervisor.new
160
- 3.times do |i|
161
- spin do
162
- sleep(0.001)
163
- supervisor.spin do
164
- sleep(0.001)
165
- result << i
166
- end
167
- end
168
- end
169
- supervisor.await
170
- end.await
171
-
172
- assert_equal [0, 1, 2], result.sort
173
- end
174
- end
175
-
176
131
  class ExceptionTest < MiniTest::Test
177
132
  def test_cross_fiber_backtrace
178
133
  error = nil
@@ -191,8 +146,8 @@ class ExceptionTest < MiniTest::Test
191
146
  rescue Exception => e
192
147
  frames << 3
193
148
  raise e
194
- end
195
- 5.times { |i| snooze }
149
+ end#.await
150
+ 5.times { snooze }
196
151
  rescue Exception => e
197
152
  error = e
198
153
  ensure
@@ -206,10 +161,9 @@ class ExceptionTest < MiniTest::Test
206
161
  spin do
207
162
  spin do
208
163
  raise 'foo'
209
- end
210
- end
211
- end
212
- 4.times { snooze }
164
+ end.await
165
+ end.await
166
+ end.await
213
167
  rescue Exception => e
214
168
  error = e
215
169
  ensure
@@ -319,6 +273,7 @@ class MoveOnAfterTest < MiniTest::Test
319
273
  snooze
320
274
  assert f.running?
321
275
  f.stop
276
+ snooze
322
277
  assert !f.running?
323
278
  end
324
279
 
data/test/test_io.rb CHANGED
@@ -194,4 +194,12 @@ class IOClassMethodsTest < MiniTest::Test
194
194
  ensure
195
195
  $stdout = orig_stdout
196
196
  end
197
+
198
+ def test_read_large_file
199
+ fn = '/tmp/test.txt'
200
+ File.open(fn, 'w') { |f| f << ('*' * 1e6) }
201
+ s = IO.read(fn)
202
+ assert_equal 1e6, s.bytesize
203
+ assert s == IO.orig_read(fn)
204
+ end
197
205
  end
data/test/test_signal.rb CHANGED
@@ -32,6 +32,7 @@ class SignalTest < MiniTest::Test
32
32
 
33
33
  snooze
34
34
  Process.kill(:HUP, Process.pid)
35
+ snooze
35
36
  assert_equal 1, count
36
37
  end
37
38
  end