polyphony 0.71 → 0.74

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +15 -11
  4. data/.github/workflows/test_io_uring.yml +32 -0
  5. data/.gitignore +3 -1
  6. data/CHANGELOG.md +33 -4
  7. data/Gemfile.lock +16 -13
  8. data/TODO.md +1 -1
  9. data/bin/pdbg +1 -1
  10. data/docs/_user-guide/all-about-timers.md +1 -1
  11. data/docs/api-reference/exception.md +5 -1
  12. data/docs/api-reference/fiber.md +2 -2
  13. data/docs/faq.md +1 -1
  14. data/docs/getting-started/overview.md +8 -8
  15. data/docs/getting-started/tutorial.md +3 -3
  16. data/docs/main-concepts/concurrency.md +1 -1
  17. data/docs/main-concepts/extending.md +3 -3
  18. data/docs/main-concepts/fiber-scheduling.md +1 -1
  19. data/examples/core/calc.rb +37 -0
  20. data/examples/core/calc_with_restart.rb +40 -0
  21. data/examples/core/calc_with_supervise.rb +37 -0
  22. data/examples/core/message_based_supervision.rb +1 -1
  23. data/examples/core/ring.rb +29 -0
  24. data/examples/io/rack_server.rb +1 -1
  25. data/examples/io/tunnel.rb +1 -1
  26. data/examples/performance/fiber_transfer.rb +1 -1
  27. data/examples/performance/line_splitting.rb +1 -1
  28. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  29. data/ext/polyphony/backend_common.c +88 -18
  30. data/ext/polyphony/backend_common.h +8 -1
  31. data/ext/polyphony/backend_io_uring.c +280 -164
  32. data/ext/polyphony/backend_io_uring_context.c +2 -1
  33. data/ext/polyphony/backend_io_uring_context.h +3 -2
  34. data/ext/polyphony/backend_libev.c +42 -38
  35. data/ext/polyphony/event.c +5 -2
  36. data/ext/polyphony/extconf.rb +25 -13
  37. data/ext/polyphony/polyphony.c +10 -1
  38. data/ext/polyphony/polyphony.h +7 -1
  39. data/ext/polyphony/queue.c +12 -7
  40. data/ext/polyphony/runqueue_ring_buffer.c +6 -3
  41. data/ext/polyphony/socket_extensions.c +5 -2
  42. data/ext/polyphony/thread.c +1 -1
  43. data/lib/polyphony/adapters/irb.rb +11 -1
  44. data/lib/polyphony/{extensions → core}/debug.rb +0 -0
  45. data/lib/polyphony/core/global_api.rb +3 -6
  46. data/lib/polyphony/core/timer.rb +2 -2
  47. data/lib/polyphony/debugger.rb +3 -3
  48. data/lib/polyphony/extensions/exception.rb +45 -0
  49. data/lib/polyphony/extensions/fiber.rb +87 -11
  50. data/lib/polyphony/extensions/io.rb +2 -2
  51. data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
  52. data/lib/polyphony/extensions/openssl.rb +20 -5
  53. data/lib/polyphony/extensions/process.rb +19 -0
  54. data/lib/polyphony/extensions/socket.rb +20 -9
  55. data/lib/polyphony/extensions/thread.rb +9 -3
  56. data/lib/polyphony/extensions/timeout.rb +10 -0
  57. data/lib/polyphony/extensions.rb +9 -0
  58. data/lib/polyphony/version.rb +1 -1
  59. data/lib/polyphony.rb +2 -4
  60. data/polyphony.gemspec +1 -1
  61. data/test/coverage.rb +2 -2
  62. data/test/test_backend.rb +15 -17
  63. data/test/test_event.rb +1 -1
  64. data/test/test_ext.rb +1 -1
  65. data/test/test_fiber.rb +31 -7
  66. data/test/test_global_api.rb +23 -14
  67. data/test/test_io.rb +5 -5
  68. data/test/test_kernel.rb +2 -2
  69. data/test/test_process_supervision.rb +1 -1
  70. data/test/test_queue.rb +6 -6
  71. data/test/test_signal.rb +20 -1
  72. data/test/test_socket.rb +45 -10
  73. data/test/test_supervise.rb +85 -0
  74. data/test/test_sync.rb +2 -2
  75. data/test/test_thread.rb +22 -2
  76. data/test/test_thread_pool.rb +2 -2
  77. data/test/test_throttler.rb +3 -3
  78. data/test/test_timer.rb +3 -3
  79. data/test/test_trace.rb +1 -1
  80. metadata +19 -9
data/test/test_backend.rb CHANGED
@@ -36,7 +36,7 @@ class BackendTest < MiniTest::Test
36
36
  f = spin { @backend.read(i, buf, 5, false, 0) }
37
37
  @backend.write(o, 'Hello world')
38
38
  return_value = f.await
39
-
39
+
40
40
  assert_equal 'Hello', buf
41
41
  assert_equal return_value, buf
42
42
  end
@@ -51,7 +51,7 @@ class BackendTest < MiniTest::Test
51
51
  snooze
52
52
  o.close
53
53
  return_value = f.await
54
-
54
+
55
55
  assert_equal 'Hello', buf
56
56
  assert_equal return_value, buf
57
57
  end
@@ -66,7 +66,7 @@ class BackendTest < MiniTest::Test
66
66
  snooze
67
67
  o.close
68
68
  return_value = f.await
69
-
69
+
70
70
  assert_equal 'Hello world', buf
71
71
  assert_equal return_value, buf
72
72
  end
@@ -77,7 +77,7 @@ class BackendTest < MiniTest::Test
77
77
  f = spin { @backend.read(i, buf, 20, false, 0) }
78
78
  @backend.write(o, 'blah')
79
79
  return_value = f.await
80
-
80
+
81
81
  assert_equal 'blah', buf
82
82
  assert_equal return_value, buf
83
83
 
@@ -85,7 +85,7 @@ class BackendTest < MiniTest::Test
85
85
  f = spin { @backend.read(i, buf, 20, false, -1) }
86
86
  @backend.write(o, 'klmn')
87
87
  return_value = f.await
88
-
88
+
89
89
  assert_equal 'abcdefghijklmn', buf
90
90
  assert_equal return_value, buf
91
91
 
@@ -93,7 +93,7 @@ class BackendTest < MiniTest::Test
93
93
  f = spin { @backend.read(i, buf, 20, false, 3) }
94
94
  @backend.write(o, 'DEF')
95
95
  return_value = f.await
96
-
96
+
97
97
  assert_equal 'abcDEF', buf
98
98
  assert_equal return_value, buf
99
99
  end
@@ -109,12 +109,12 @@ class BackendTest < MiniTest::Test
109
109
 
110
110
  o << data
111
111
  o.close
112
-
112
+
113
113
  buf = +''
114
114
 
115
115
  @backend.read(i, buf, 4096, false, -1)
116
116
  assert_equal 4096, buf.bytesize
117
-
117
+
118
118
  @backend.read(i, buf, 1, false, -1)
119
119
  assert_equal 4097, buf.bytesize
120
120
 
@@ -129,7 +129,7 @@ class BackendTest < MiniTest::Test
129
129
  @backend.post_fork
130
130
  exit(42)
131
131
  end
132
-
132
+
133
133
  result = @backend.waitpid(pid)
134
134
  assert_equal [pid, 42], result
135
135
  end
@@ -243,16 +243,14 @@ class BackendTest < MiniTest::Test
243
243
  end
244
244
 
245
245
  def test_timer_loop
246
- skip unless IS_LINUX
247
-
248
- i = 0
246
+ counter = 0
249
247
  f = spin do
250
- @backend.timer_loop(0.01) { i += 1 }
248
+ @backend.timer_loop(0.01) { counter += 1 }
251
249
  end
252
250
  @backend.sleep(0.05)
253
251
  f.stop
254
252
  f.await # TODO: check why this test sometimes segfaults if we don't a<wait fiber
255
- assert_in_range 4..6, i
253
+ assert_in_range 4..6, counter if IS_LINUX
256
254
  end
257
255
 
258
256
  class MyTimeoutException < Exception
@@ -427,7 +425,7 @@ class BackendTest < MiniTest::Test
427
425
  counter = 0
428
426
 
429
427
  @backend.idle_proc = proc { counter += 1 }
430
-
428
+
431
429
  3.times { snooze }
432
430
  assert_equal 0, counter
433
431
 
@@ -488,7 +486,7 @@ class BackendChainTest < MiniTest::Test
488
486
 
489
487
  snooze
490
488
  client = TCPSocket.new('127.0.0.1', port)
491
-
489
+
492
490
  result = Thread.backend.chain(
493
491
  [:send, client, 'hello', 0],
494
492
  [:send, client, " world\n", 0]
@@ -512,7 +510,7 @@ class BackendChainTest < MiniTest::Test
512
510
  while true
513
511
  len = o.splice(from, 8192)
514
512
  break if len == 0
515
-
513
+
516
514
  backend.chain(
517
515
  [:write, to, chunk_header(len)],
518
516
  [:splice, i, to, len]
data/test/test_event.rb CHANGED
@@ -25,7 +25,7 @@ class EventTest < MiniTest::Test
25
25
  def test_that_event_coalesces_signals
26
26
  count = 0
27
27
  a = Polyphony::Event.new
28
-
28
+
29
29
  coproc = spin {
30
30
  loop {
31
31
  a.await
data/test/test_ext.rb CHANGED
@@ -73,7 +73,7 @@ class KernelTest < MiniTest::Test
73
73
  data = `>&2 echo "error"`
74
74
  $stderr.rewind
75
75
  $stderr = prev_stderr
76
-
76
+
77
77
  assert_equal '', data
78
78
  assert_equal "error\n", err_io.read
79
79
  ensure
data/test/test_fiber.rb CHANGED
@@ -47,7 +47,7 @@ class FiberTest < MiniTest::Test
47
47
  f1 = spin { :foo }
48
48
  f2 = spin { :bar }
49
49
  4.times { snooze }
50
-
50
+
51
51
  assert_equal [:foo, :bar], Fiber.await(f1, f2)
52
52
  end
53
53
 
@@ -739,7 +739,7 @@ class FiberTest < MiniTest::Test
739
739
  def test_setup_raw
740
740
  buffer = []
741
741
  f = Fiber.new { buffer << receive }
742
-
742
+
743
743
  assert_nil f.thread
744
744
  snooze
745
745
  f.setup_raw
@@ -771,10 +771,11 @@ class FiberTest < MiniTest::Test
771
771
 
772
772
  snooze
773
773
  assert_equal parent, child.parent
774
- child.detach
774
+ result = child.detach
775
+ assert_equal result, child
775
776
  assert_equal Fiber.current, child.parent
776
777
  parent.await
777
-
778
+
778
779
  child << :bye
779
780
  child.await
780
781
 
@@ -807,7 +808,7 @@ class FiberTest < MiniTest::Test
807
808
  child.attach_to(new_parent)
808
809
  assert_equal new_parent, child.parent
809
810
  parent.await
810
-
811
+
811
812
  child << :bye
812
813
  new_parent << :bye_new_parent
813
814
  snooze
@@ -820,6 +821,29 @@ class FiberTest < MiniTest::Test
820
821
  :bye_new_parent
821
822
  ], buf
822
823
  end
824
+
825
+ def test_attach_all_children_to
826
+ children = []
827
+ f1 = spin do
828
+ 3.times {
829
+ children << spin { receive }
830
+ }
831
+ Fiber.current.parent << :ok
832
+ receive
833
+ end
834
+
835
+ result = receive
836
+ assert_equal :ok, result
837
+ assert_equal 3, children.size
838
+
839
+ f2 = spin { supervise }
840
+ f1.attach_all_children_to(f2)
841
+
842
+ snooze
843
+
844
+ assert_equal [], f1.children
845
+ assert_equal children, f2.children
846
+ end
823
847
  end
824
848
 
825
849
  class MailboxTest < MiniTest::Test
@@ -1126,7 +1150,7 @@ class RestartTest < MiniTest::Test
1126
1150
  assert_equal [1], buffer
1127
1151
  snooze
1128
1152
  assert_equal [1, 1], buffer
1129
-
1153
+
1130
1154
  f << 'foo'
1131
1155
  sleep 0.1
1132
1156
  assert_equal [1, 1, 2], buffer
@@ -1252,7 +1276,7 @@ class DebugTest < MiniTest::Test
1252
1276
  assert_equal true, f.__parked__?
1253
1277
  10.times { snooze }
1254
1278
  assert_equal [], buf
1255
-
1279
+
1256
1280
  f.__unpark__
1257
1281
  assert_nil f.__parked__?
1258
1282
  10.times { snooze }
@@ -162,7 +162,7 @@ class MoveOnAfterTest < MiniTest::Test
162
162
  end
163
163
  t1 = Time.now
164
164
  assert_equal 1, o
165
- assert_in_range 0.008..0.015, t1 - t0
165
+ assert_in_range 0.008..0.015, t1 - t0 if IS_LINUX
166
166
 
167
167
  t0 = Time.now
168
168
  o = move_on_after(0.05, with_value: 1) do
@@ -172,7 +172,7 @@ class MoveOnAfterTest < MiniTest::Test
172
172
  end
173
173
  t1 = Time.now
174
174
  assert_equal 2, o
175
- assert_in_range 0.008..0.013, t1 - t0
175
+ assert_in_range 0.008..0.013, t1 - t0 if IS_LINUX
176
176
  end
177
177
  end
178
178
 
@@ -280,7 +280,7 @@ class SpinLoopTest < MiniTest::Test
280
280
  def test_spin_loop_location
281
281
  location = /^#{__FILE__}:#{__LINE__ + 1}/
282
282
  f = spin_loop { snooze }
283
-
283
+
284
284
  assert_match location, f.location
285
285
  end
286
286
 
@@ -297,7 +297,7 @@ class SpinLoopTest < MiniTest::Test
297
297
  f = spin_loop(rate: 100) { buffer << (counter += 1) }
298
298
  sleep 0.02
299
299
  f.stop
300
- assert_in_range 1..3, counter
300
+ assert_in_range 1..3, counter if IS_LINUX
301
301
  end
302
302
 
303
303
  def test_spin_loop_with_interval
@@ -307,7 +307,7 @@ class SpinLoopTest < MiniTest::Test
307
307
  f = spin_loop(interval: 0.01) { buffer << (counter += 1) }
308
308
  sleep 0.02
309
309
  f.stop
310
- assert_in_range 1..3, counter
310
+ assert_in_range 1..3, counter if IS_LINUX
311
311
  end
312
312
 
313
313
  def test_spin_loop_break
@@ -386,10 +386,10 @@ class ThrottledLoopTest < MiniTest::Test
386
386
  counter = 0
387
387
  t0 = Time.now
388
388
  f = spin do
389
- throttled_loop(100) { buffer << (counter += 1) }
389
+ throttled_loop(10) { buffer << (counter += 1) }
390
390
  end
391
- sleep 0.03
392
- assert_in_range 2..4, counter
391
+ sleep 0.3
392
+ assert_in_range 2..4, counter if IS_LINUX
393
393
  end
394
394
 
395
395
  def test_throttled_loop_with_count
@@ -402,7 +402,7 @@ class ThrottledLoopTest < MiniTest::Test
402
402
  f.await
403
403
  t1 = Time.now
404
404
  assert_in_range 0.075..0.15, t1 - t0 if IS_LINUX
405
- assert_equal [1, 2, 3, 4, 5], buffer
405
+ assert_equal [1, 2, 3, 4, 5], buffer
406
406
  end
407
407
  end
408
408
 
@@ -412,13 +412,11 @@ class GlobalAPIEtcTest < MiniTest::Test
412
412
  f = after(0.001) { buffer << 2 }
413
413
  snooze
414
414
  assert_equal [], buffer
415
- sleep 0.001
415
+ sleep 0.0015
416
416
  assert_equal [2], buffer
417
417
  end
418
418
 
419
419
  def test_every
420
- skip unless IS_LINUX
421
-
422
420
  buffer = []
423
421
  t0 = Time.now
424
422
  f = spin do
@@ -426,14 +424,25 @@ class GlobalAPIEtcTest < MiniTest::Test
426
424
  end
427
425
  sleep 0.05
428
426
  f.stop
429
- assert_in_range 4..6, buffer.size
427
+ assert_in_range 4..6, buffer.size if IS_LINUX
428
+ end
429
+
430
+ def test_every_with_slow_op
431
+ buffer = []
432
+ t0 = Time.now
433
+ f = spin do
434
+ every(0.01) { sleep 0.05; buffer << 1 }
435
+ end
436
+ sleep 0.15
437
+ f.stop
438
+ assert_in_range 2..3, buffer.size if IS_LINUX
430
439
  end
431
440
 
432
441
  def test_sleep
433
442
  t0 = Time.now
434
443
  sleep 0.1
435
444
  elapsed = Time.now - t0
436
- assert (0.05..0.15).include? elapsed if IS_LINUX
445
+ assert_in_range 0.05..0.15, elapsed if IS_LINUX
437
446
 
438
447
  f = spin { sleep }
439
448
  snooze
data/test/test_io.rb CHANGED
@@ -107,10 +107,10 @@ class IOTest < MiniTest::Test
107
107
  sleep 0.01
108
108
  o << 'hi'
109
109
  }
110
- assert_equal 'hi', i.readpartial(2)
110
+ assert_equal 'hi', i.readpartial(2)
111
111
  o.close
112
112
 
113
- assert_raises(EOFError) { i.readpartial(1) }
113
+ assert_raises(EOFError) { i.readpartial(1) }
114
114
  end
115
115
 
116
116
  def test_gets
@@ -132,7 +132,7 @@ class IOTest < MiniTest::Test
132
132
  f << Fiber.current
133
133
  sleep 0.05
134
134
  assert_equal [], buf
135
-
135
+
136
136
  o << "ulous\n"
137
137
  receive
138
138
  assert_equal ["fabulous\n"], buf
@@ -354,9 +354,9 @@ class IOClassMethodsTest < MiniTest::Test
354
354
  skip unless IS_LINUX
355
355
 
356
356
  counter = 0
357
- timer = spin { throttled_loop(200) { counter += 1 } }
357
+ timer = spin { throttled_loop(20) { counter += 1 } }
358
358
 
359
- IO.popen('sleep 0.05') { |io| io.read(8192) }
359
+ IO.popen('sleep 0.5') { |io| io.read(8192) }
360
360
  assert(counter >= 5)
361
361
 
362
362
  result = nil
data/test/test_kernel.rb CHANGED
@@ -51,8 +51,8 @@ class KernelTest < MiniTest::Test
51
51
  counter = 0
52
52
  timer = spin { throttled_loop(200) { counter += 1 } }
53
53
 
54
- `sleep 0.01`
55
- assert(counter >= 2)
54
+ `sleep 0.05`
55
+ assert_in_range 8..14, counter if IS_LINUX
56
56
 
57
57
  result = `echo "hello"`
58
58
  assert_equal "hello\n", result
@@ -65,7 +65,7 @@ class ProcessSupervisionTest < MiniTest::Test
65
65
 
66
66
  supervisor = spin { supervise(watcher) }
67
67
 
68
- sleep 0.05
68
+ sleep 0.1
69
69
  supervisor.terminate
70
70
  supervisor.await
71
71
 
data/test/test_queue.rb CHANGED
@@ -70,7 +70,7 @@ class QueueTest < MiniTest::Test
70
70
 
71
71
  def test_empty?
72
72
  assert @queue.empty?
73
-
73
+
74
74
  @queue << :foo
75
75
  assert !@queue.empty?
76
76
 
@@ -82,7 +82,7 @@ class QueueTest < MiniTest::Test
82
82
  f1 = spin { @queue.shift }
83
83
  f2 = spin { @queue.shift }
84
84
  f3 = spin { @queue.shift }
85
-
85
+
86
86
  # let fibers run
87
87
  snooze
88
88
 
@@ -99,7 +99,7 @@ class QueueTest < MiniTest::Test
99
99
 
100
100
  def test_fiber_removal_from_queue_simple
101
101
  f1 = spin { @queue.shift }
102
-
102
+
103
103
  # let fibers run
104
104
  snooze
105
105
 
@@ -114,11 +114,11 @@ class QueueTest < MiniTest::Test
114
114
  assert_equal 0, @queue.size
115
115
 
116
116
  @queue.push 1
117
-
117
+
118
118
  assert_equal 1, @queue.size
119
119
 
120
120
  @queue.push 2
121
-
121
+
122
122
  assert_equal 2, @queue.size
123
123
 
124
124
  @queue.shift
@@ -231,7 +231,7 @@ class CappedQueueTest < MiniTest::Test
231
231
  i = 0
232
232
  spin_loop do
233
233
  i += 1
234
- snooze
234
+ snooze
235
235
  end
236
236
 
237
237
  5.times { snooze }
data/test/test_signal.rb CHANGED
@@ -93,4 +93,23 @@ class SignalTrapTest < Minitest::Test
93
93
  buffer = i.read
94
94
  assert_equal "INT\n", buffer
95
95
  end
96
- end
96
+
97
+ def test_busy_signal_handling
98
+ i, o = IO.pipe
99
+ pid = Polyphony.fork do
100
+ main = Fiber.current
101
+ trap('INT') { o.puts 'INT'; o.close; main.stop }
102
+ i.close
103
+ f1 = spin_loop { snooze }
104
+ f2 = spin_loop { snooze }
105
+ f1.await
106
+ end
107
+
108
+ o.close
109
+ sleep 0.1
110
+ Process.kill('INT', pid)
111
+ Thread.current.backend.waitpid(pid)
112
+ buffer = i.read
113
+ assert_equal "INT\n", buffer
114
+ end
115
+ end
data/test/test_socket.rb CHANGED
@@ -9,9 +9,16 @@ class SocketTest < MiniTest::Test
9
9
  super
10
10
  end
11
11
 
12
+ def start_tcp_server_on_random_port(host = '127.0.0.1')
13
+ port = rand(1100..60000)
14
+ server = TCPServer.new(host, port)
15
+ [port, server]
16
+ rescue Errno::EADDRINUSE
17
+ retry
18
+ end
19
+
12
20
  def test_tcp
13
- port = rand(1234..5678)
14
- server = TCPServer.new('127.0.0.1', port)
21
+ port, server = start_tcp_server_on_random_port
15
22
  server_fiber = spin do
16
23
  while (socket = server.accept)
17
24
  spin do
@@ -33,9 +40,41 @@ class SocketTest < MiniTest::Test
33
40
  server&.close
34
41
  end
35
42
 
43
+ def test_tcpsocket_open_with_hostname
44
+ client = TCPSocket.open('google.com', 80)
45
+ client.write("GET / HTTP/1.0\r\nHost: google.com\r\n\r\n")
46
+ result = nil
47
+ move_on_after(1) {
48
+ result = client.read
49
+ }
50
+ assert result =~ /HTTP\/1.0 301 Moved Permanently/
51
+ end
52
+
53
+ def test_tcp_ipv6
54
+ port, server = start_tcp_server_on_random_port('::1')
55
+ server_fiber = spin do
56
+ while (socket = server.accept)
57
+ spin do
58
+ while (data = socket.gets(8192))
59
+ socket << data
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ snooze
66
+ client = TCPSocket.new('::1', port)
67
+ client.write("1234\n")
68
+ assert_equal "1234\n", client.recv(8192)
69
+ client.close
70
+ ensure
71
+ server_fiber&.stop
72
+ server_fiber&.await
73
+ server&.close
74
+ end
75
+
36
76
  def test_read
37
- port = rand(1234..5678)
38
- server = TCPServer.new('127.0.0.1', port)
77
+ port, server = start_tcp_server_on_random_port
39
78
  server_fiber = spin do
40
79
  while (socket = server.accept)
41
80
  spin do
@@ -74,9 +113,7 @@ class SocketTest < MiniTest::Test
74
113
 
75
114
  # sending multiple strings at once
76
115
  def test_sendv
77
- port = rand(1234..5678)
78
- server = TCPServer.new('127.0.0.1', port)
79
-
116
+ port, server = start_tcp_server_on_random_port
80
117
  server_fiber = spin do
81
118
  while (socket = server.accept)
82
119
  spin do
@@ -100,9 +137,7 @@ class SocketTest < MiniTest::Test
100
137
 
101
138
 
102
139
  def test_feed_loop
103
- port = rand(1234..5678)
104
- server = TCPServer.new('127.0.0.1', port)
105
-
140
+ port, server = start_tcp_server_on_random_port
106
141
  server_fiber = spin do
107
142
  reader = MessagePack::Unpacker.new
108
143
  while (socket = server.accept)
@@ -135,6 +135,44 @@ class SuperviseTest < MiniTest::Test
135
135
  assert_equal supervisor, f3.parent
136
136
  end
137
137
 
138
+ def test_supervise_with_restart_true
139
+ buffer = []
140
+ f1 = spin(:f1) do
141
+ buffer << receive
142
+ rescue => e
143
+ buffer << e
144
+ e
145
+ end
146
+ supervisor = spin(:supervisor) { supervise(f1, restart: true) }
147
+
148
+ snooze
149
+ f1 << 'foo'
150
+ f1.await
151
+ snooze
152
+ assert_equal ['foo'], buffer
153
+
154
+ 10.times { snooze }
155
+
156
+ assert_equal 1, supervisor.children.size
157
+ f2 = supervisor.children.first
158
+ assert f1 != f2
159
+ assert_equal :f1, f2.tag
160
+ assert_equal supervisor, f2.parent
161
+
162
+ e = RuntimeError.new('bar')
163
+ f2.raise(e)
164
+ f2.await rescue nil
165
+ 3.times { snooze }
166
+ assert_equal ['foo', e], buffer
167
+
168
+ assert_equal 1, supervisor.children.size
169
+ f3 = supervisor.children.first
170
+ assert f2 != f3
171
+ assert f1 != f3
172
+ assert_equal :f1, f3.tag
173
+ assert_equal supervisor, f3.parent
174
+ end
175
+
138
176
  def test_supervise_with_restart_on_error
139
177
  buffer = []
140
178
  f1 = spin(:f1) do
@@ -184,4 +222,51 @@ class SuperviseTest < MiniTest::Test
184
222
 
185
223
  assert_equal [], buffer
186
224
  end
225
+
226
+ def test_supervise_without_explicit_fibers
227
+ buffer = []
228
+ first = nil
229
+ supervisor = spin do
230
+ 3.times do |i|
231
+ f = spin do
232
+ first = Fiber.current if i == 0
233
+ receive
234
+ buffer << i
235
+ end
236
+ end
237
+ Fiber.current.parent << :ok
238
+ supervise(restart: :always)
239
+ end
240
+ msg = receive
241
+ assert_equal :ok, msg
242
+ assert_equal 3, supervisor.children.size
243
+
244
+ sleep 0.1
245
+ assert_equal [], buffer
246
+
247
+ old_first = first
248
+ first << :foo
249
+ first = nil
250
+ snooze
251
+ assert_equal [0], buffer
252
+
253
+ snooze
254
+ assert_equal 3, supervisor.children.size
255
+ snooze
256
+ assert first
257
+ assert first != old_first
258
+ end
259
+
260
+ def test_supervise_with_added_fibers
261
+ buffer = []
262
+ supervisor = spin do
263
+ supervise { |f, r| buffer << [f, r] }
264
+ end
265
+ snooze
266
+ f1 = supervisor.spin { snooze; :foo }
267
+ f2 = supervisor.spin { snooze; :bar }
268
+ Fiber.await(f1, f2)
269
+ snooze
270
+ assert_equal [[f1, :foo], [f2, :bar]], buffer
271
+ end
187
272
  end
data/test/test_sync.rb CHANGED
@@ -104,12 +104,12 @@ class MutexTest < MiniTest::Test
104
104
  snooze
105
105
  assert !lock.locked?
106
106
  a << Fiber.current
107
-
107
+
108
108
  receive
109
109
  assert lock.locked?
110
110
 
111
111
  a << Fiber.current
112
-
112
+
113
113
  receive
114
114
  assert !lock.locked?
115
115
  end
data/test/test_thread.rb CHANGED
@@ -180,7 +180,7 @@ class ThreadTest < MiniTest::Test
180
180
  assert_equal count, GC.count
181
181
  sleep 0.05
182
182
  assert_equal count, GC.count
183
-
183
+
184
184
  return unless IS_LINUX
185
185
 
186
186
  # The idle tasks are ran at most once per fiber switch, before the backend
@@ -207,7 +207,7 @@ class ThreadTest < MiniTest::Test
207
207
  counter = 0
208
208
 
209
209
  Thread.current.on_idle { counter += 1 }
210
-
210
+
211
211
  3.times { snooze }
212
212
  assert_equal 0, counter
213
213
 
@@ -220,4 +220,24 @@ class ThreadTest < MiniTest::Test
220
220
  3.times { snooze }
221
221
  assert_equal 2, counter
222
222
  end
223
+
224
+ def test_cross_thread_receive
225
+ buf = []
226
+ f = Fiber.current
227
+ t = Thread.new do
228
+ f << true
229
+ while (msg = receive)
230
+ buf << msg
231
+ end
232
+ end
233
+
234
+ receive # wait for thread to be ready
235
+ t << 1
236
+ t << 2
237
+ t << 3
238
+ t << nil
239
+
240
+ t.join
241
+ assert_equal [1, 2, 3], buf
242
+ end
223
243
  end