polyphony 0.71 → 0.74

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 (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