polyphony 0.40 → 0.43.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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +29 -2
  6. data/Gemfile.lock +13 -10
  7. data/README.md +0 -1
  8. data/Rakefile +3 -3
  9. data/TODO.md +27 -97
  10. data/docs/_config.yml +56 -7
  11. data/docs/_sass/custom/custom.scss +6 -26
  12. data/docs/_sass/overrides.scss +0 -46
  13. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  14. data/docs/_user-guide/index.md +9 -0
  15. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  16. data/docs/api-reference/fiber.md +2 -2
  17. data/docs/api-reference/index.md +9 -0
  18. data/docs/api-reference/polyphony-process.md +1 -1
  19. data/docs/api-reference/thread.md +1 -1
  20. data/docs/faq.md +21 -11
  21. data/docs/favicon.ico +0 -0
  22. data/docs/getting-started/index.md +10 -0
  23. data/docs/getting-started/installing.md +2 -6
  24. data/docs/getting-started/overview.md +486 -0
  25. data/docs/getting-started/tutorial.md +27 -19
  26. data/docs/index.md +6 -2
  27. data/docs/main-concepts/concurrency.md +0 -5
  28. data/docs/main-concepts/design-principles.md +69 -21
  29. data/docs/main-concepts/extending.md +1 -1
  30. data/docs/main-concepts/index.md +9 -0
  31. data/docs/polyphony-logo.png +0 -0
  32. data/examples/adapters/redis_blpop.rb +12 -0
  33. data/examples/core/01-spinning-up-fibers.rb +1 -0
  34. data/examples/core/03-interrupting.rb +4 -1
  35. data/examples/core/04-handling-signals.rb +19 -0
  36. data/examples/core/xx-agent.rb +102 -0
  37. data/examples/core/xx-sleeping.rb +14 -6
  38. data/examples/io/xx-irb.rb +1 -1
  39. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  40. data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
  41. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  42. data/examples/performance/xx-array.rb +11 -0
  43. data/examples/performance/xx-fiber-switch.rb +9 -0
  44. data/examples/performance/xx-snooze.rb +15 -0
  45. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  46. data/ext/{gyro → polyphony}/fiber.c +15 -22
  47. data/ext/{gyro → polyphony}/libev.c +0 -0
  48. data/ext/{gyro → polyphony}/libev.h +0 -0
  49. data/ext/polyphony/libev_agent.c +725 -0
  50. data/ext/polyphony/libev_queue.c +217 -0
  51. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +12 -37
  52. data/ext/polyphony/polyphony.h +90 -0
  53. data/ext/polyphony/polyphony_ext.c +21 -0
  54. data/ext/{gyro → polyphony}/thread.c +34 -151
  55. data/ext/{gyro → polyphony}/tracing.c +1 -1
  56. data/lib/polyphony.rb +19 -12
  57. data/lib/polyphony/adapters/irb.rb +1 -1
  58. data/lib/polyphony/adapters/postgres.rb +6 -5
  59. data/lib/polyphony/adapters/process.rb +5 -5
  60. data/lib/polyphony/adapters/redis.rb +3 -2
  61. data/lib/polyphony/adapters/trace.rb +28 -28
  62. data/lib/polyphony/core/channel.rb +3 -3
  63. data/lib/polyphony/core/exceptions.rb +1 -1
  64. data/lib/polyphony/core/global_api.rb +13 -11
  65. data/lib/polyphony/core/resource_pool.rb +3 -3
  66. data/lib/polyphony/core/sync.rb +2 -2
  67. data/lib/polyphony/core/thread_pool.rb +6 -6
  68. data/lib/polyphony/core/throttler.rb +13 -6
  69. data/lib/polyphony/event.rb +27 -0
  70. data/lib/polyphony/extensions/core.rb +22 -14
  71. data/lib/polyphony/extensions/fiber.rb +4 -4
  72. data/lib/polyphony/extensions/io.rb +59 -25
  73. data/lib/polyphony/extensions/openssl.rb +36 -16
  74. data/lib/polyphony/extensions/socket.rb +28 -10
  75. data/lib/polyphony/extensions/thread.rb +16 -9
  76. data/lib/polyphony/net.rb +9 -9
  77. data/lib/polyphony/version.rb +1 -1
  78. data/polyphony.gemspec +3 -3
  79. data/test/helper.rb +12 -1
  80. data/test/test_agent.rb +130 -0
  81. data/test/{test_async.rb → test_event.rb} +13 -7
  82. data/test/test_ext.rb +25 -4
  83. data/test/test_fiber.rb +19 -10
  84. data/test/test_global_api.rb +6 -6
  85. data/test/test_io.rb +46 -24
  86. data/test/test_queue.rb +74 -0
  87. data/test/test_signal.rb +3 -40
  88. data/test/test_socket.rb +34 -0
  89. data/test/test_thread.rb +37 -16
  90. data/test/test_trace.rb +6 -5
  91. metadata +39 -41
  92. data/docs/_includes/nav.html +0 -51
  93. data/docs/_includes/prevnext.html +0 -17
  94. data/docs/_layouts/default.html +0 -106
  95. data/docs/api-reference.md +0 -11
  96. data/docs/api-reference/gyro-async.md +0 -57
  97. data/docs/api-reference/gyro-child.md +0 -29
  98. data/docs/api-reference/gyro-queue.md +0 -44
  99. data/docs/api-reference/gyro-timer.md +0 -51
  100. data/docs/api-reference/gyro.md +0 -25
  101. data/docs/getting-started.md +0 -10
  102. data/docs/main-concepts.md +0 -10
  103. data/docs/user-guide.md +0 -10
  104. data/examples/core/forever_sleep.rb +0 -19
  105. data/ext/gyro/async.c +0 -132
  106. data/ext/gyro/child.c +0 -108
  107. data/ext/gyro/gyro.h +0 -158
  108. data/ext/gyro/gyro_ext.c +0 -33
  109. data/ext/gyro/io.c +0 -457
  110. data/ext/gyro/queue.c +0 -146
  111. data/ext/gyro/selector.c +0 -205
  112. data/ext/gyro/signal.c +0 -99
  113. data/ext/gyro/socket.c +0 -213
  114. data/ext/gyro/timer.c +0 -115
  115. data/test/test_timer.rb +0 -56
@@ -8,26 +8,30 @@ class ::Thread
8
8
 
9
9
  alias_method :orig_initialize, :initialize
10
10
  def initialize(*args, &block)
11
- @join_wait_queue = Gyro::Queue.new
11
+ @join_wait_queue = []
12
+ @finalization_mutex = Mutex.new
12
13
  @args = args
13
14
  @block = block
14
- @finalization_mutex = Mutex.new
15
15
  orig_initialize { execute }
16
16
  end
17
17
 
18
18
  def execute
19
+ # agent must be created in the context of the new thread, therefore it
20
+ # cannot be created in Thread#initialize
21
+ @agent = Polyphony::LibevAgent.new
19
22
  setup
20
23
  @ready = true
21
24
  result = @block.(*@args)
22
25
  rescue Polyphony::MoveOn, Polyphony::Terminate => e
23
26
  result = e.value
24
- rescue Exception => e
25
- result = e
27
+ rescue Exception => result
26
28
  ensure
27
29
  @ready = true
28
30
  finalize(result)
29
31
  end
30
32
 
33
+ attr_accessor :agent
34
+
31
35
  def setup
32
36
  @main_fiber = Fiber.current
33
37
  @main_fiber.setup_main_fiber
@@ -44,24 +48,25 @@ class ::Thread
44
48
  @result = result
45
49
  signal_waiters(result)
46
50
  end
47
- stop_event_selector
51
+ @agent.finalize
48
52
  end
49
53
 
50
54
  def signal_waiters(result)
51
- @join_wait_queue.shift_each { |w| w.signal(result) }
55
+ @join_wait_queue.each { |w| w.signal(result) }
52
56
  end
53
57
 
54
58
  alias_method :orig_join, :join
55
59
  def join(timeout = nil)
56
- async = Fiber.current.auto_async
60
+ watcher = Fiber.current.auto_watcher
61
+
57
62
  @finalization_mutex.synchronize do
58
63
  if @terminated
59
64
  @result.is_a?(Exception) ? (raise @result) : (return @result)
60
65
  else
61
- @join_wait_queue.push(async)
66
+ @join_wait_queue << watcher
62
67
  end
63
68
  end
64
- timeout ? move_on_after(timeout) { async.await } : async.await
69
+ timeout ? move_on_after(timeout) { watcher.await } : watcher.await
65
70
  end
66
71
  alias_method :await, :join
67
72
 
@@ -78,6 +83,8 @@ class ::Thread
78
83
 
79
84
  alias_method :orig_kill, :kill
80
85
  def kill
86
+ return if @terminated
87
+
81
88
  raise Polyphony::Terminate
82
89
  end
83
90
 
@@ -4,6 +4,7 @@ require_relative './extensions/socket'
4
4
  require_relative './extensions/openssl'
5
5
 
6
6
  module Polyphony
7
+ # A more elegant networking API
7
8
  module Net
8
9
  class << self
9
10
  def tcp_connect(host, port, opts = {})
@@ -17,11 +18,11 @@ module Polyphony
17
18
  socket
18
19
  end
19
20
  end
20
-
21
+
21
22
  def tcp_listen(host = nil, port = nil, opts = {})
22
23
  host ||= '0.0.0.0'
23
24
  raise 'Port number not specified' unless port
24
-
25
+
25
26
  socket = socket_from_options(host, port, opts)
26
27
  if opts[:secure_context] || opts[:secure]
27
28
  secure_server(socket, opts[:secure_context], opts)
@@ -29,7 +30,7 @@ module Polyphony
29
30
  socket
30
31
  end
31
32
  end
32
-
33
+
33
34
  def socket_from_options(host, port, opts)
34
35
  ::Socket.new(:INET, :STREAM).tap do |s|
35
36
  s.reuse_addr if opts[:reuse_addr]
@@ -39,19 +40,19 @@ module Polyphony
39
40
  s.listen(0)
40
41
  end
41
42
  end
42
-
43
+
43
44
  def secure_socket(socket, context, opts)
44
45
  context ||= OpenSSL::SSL::SSLContext.new
45
46
  setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
46
47
  socket = secure_socket_wrapper(socket, context)
47
-
48
+
48
49
  socket.tap do |s|
49
50
  s.hostname = opts[:host] if opts[:host]
50
51
  s.connect
51
52
  s.post_connection_check(opts[:host]) if opts[:host]
52
53
  end
53
54
  end
54
-
55
+
55
56
  def secure_socket_wrapper(socket, context)
56
57
  if context
57
58
  OpenSSL::SSL::SSLSocket.new(socket, context)
@@ -59,12 +60,12 @@ module Polyphony
59
60
  OpenSSL::SSL::SSLSocket.new(socket)
60
61
  end
61
62
  end
62
-
63
+
63
64
  def secure_server(socket, context, opts)
64
65
  setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
65
66
  OpenSSL::SSL::SSLServer.new(socket, context)
66
67
  end
67
-
68
+
68
69
  def setup_alpn(context, protocols)
69
70
  context.alpn_protocols = protocols
70
71
  context.alpn_select_cb = lambda do |peer_protocols|
@@ -74,4 +75,3 @@ module Polyphony
74
75
  end
75
76
  end
76
77
  end
77
-
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.40'
4
+ VERSION = '0.43.2'
5
5
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  }
18
18
  s.rdoc_options = ["--title", "polyphony", "--main", "README.md"]
19
19
  s.extra_rdoc_files = ["README.md"]
20
- s.extensions = ["ext/gyro/extconf.rb"]
20
+ s.extensions = ["ext/polyphony/extconf.rb"]
21
21
  s.require_paths = ["lib"]
22
22
  s.required_ruby_version = '>= 2.6'
23
23
 
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency 'minitest', '5.13.0'
27
27
  s.add_development_dependency 'minitest-reporters', '1.4.2'
28
28
  s.add_development_dependency 'simplecov', '0.17.1'
29
- s.add_development_dependency 'rubocop', '0.80.0'
29
+ s.add_development_dependency 'rubocop', '0.85.1'
30
30
  s.add_development_dependency 'pg', '1.1.4'
31
31
  s.add_development_dependency 'rake-compiler', '1.0.5'
32
32
  s.add_development_dependency 'redis', '4.1.0'
@@ -36,5 +36,5 @@ Gem::Specification.new do |s|
36
36
  s.add_development_dependency 'jekyll', '~>3.8.6'
37
37
  s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
38
38
  s.add_development_dependency 'jekyll-seo-tag', '~>2.6.1'
39
- s.add_development_dependency 'just-the-docs', '~>0.2.7'
39
+ s.add_development_dependency 'just-the-docs', '~>0.3.0'
40
40
  end
@@ -18,6 +18,10 @@ Minitest::Reporters.use! [
18
18
  Minitest::Reporters::SpecReporter.new
19
19
  ]
20
20
 
21
+ class ::Fiber
22
+ attr_writer :auto_watcher
23
+ end
24
+
21
25
  class MiniTest::Test
22
26
  def setup
23
27
  # puts "* setup #{self.name}"
@@ -26,13 +30,20 @@ class MiniTest::Test
26
30
  exit!
27
31
  end
28
32
  Fiber.current.setup_main_fiber
33
+ Fiber.current.instance_variable_set(:@auto_watcher, nil)
34
+ Thread.current.agent = Polyphony::LibevAgent.new
29
35
  sleep 0
30
36
  end
31
37
 
32
38
  def teardown
33
- #puts "* teardown #{self.name}"
39
+ # puts "* teardown #{self.name.inspect} Fiber.current: #{Fiber.current.inspect}"
34
40
  Fiber.current.terminate_all_children
35
41
  Fiber.current.await_all_children
42
+ Fiber.current.auto_watcher = nil
43
+ rescue => e
44
+ puts e
45
+ puts e.backtrace.join("\n")
46
+ exit!
36
47
  end
37
48
  end
38
49
 
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class AgentTest < MiniTest::Test
6
+ def setup
7
+ super
8
+ @prev_agent = Thread.current.agent
9
+ @agent = Polyphony::LibevAgent.new
10
+ Thread.current.agent = @agent
11
+ end
12
+
13
+ def teardown
14
+ @agent.finalize
15
+ Thread.current.agent = @prev_agent
16
+ end
17
+
18
+ def test_sleep
19
+ count = 0
20
+ t0 = Time.now
21
+ spin {
22
+ @agent.sleep 0.01
23
+ count += 1
24
+ @agent.sleep 0.01
25
+ count += 1
26
+ @agent.sleep 0.01
27
+ count += 1
28
+ }.await
29
+ assert Time.now - t0 >= 0.03
30
+ assert_equal 3, count
31
+ end
32
+
33
+ def test_write_read_partial
34
+ i, o = IO.pipe
35
+ buf = +''
36
+ f = spin { @agent.read(i, buf, 5, false) }
37
+ @agent.write(o, 'Hello world')
38
+ return_value = f.await
39
+
40
+ assert_equal 'Hello', buf
41
+ assert_equal return_value, buf
42
+ end
43
+
44
+ def test_write_read_to_eof_limited_buffer
45
+ i, o = IO.pipe
46
+ buf = +''
47
+ f = spin { @agent.read(i, buf, 5, true) }
48
+ @agent.write(o, 'Hello')
49
+ snooze
50
+ @agent.write(o, ' world')
51
+ snooze
52
+ o.close
53
+ return_value = f.await
54
+
55
+ assert_equal 'Hello', buf
56
+ assert_equal return_value, buf
57
+ end
58
+
59
+ def test_write_read_to_eof
60
+ i, o = IO.pipe
61
+ buf = +''
62
+ f = spin { @agent.read(i, buf, 10**6, true) }
63
+ @agent.write(o, 'Hello')
64
+ snooze
65
+ @agent.write(o, ' world')
66
+ snooze
67
+ o.close
68
+ return_value = f.await
69
+
70
+ assert_equal 'Hello world', buf
71
+ assert_equal return_value, buf
72
+ end
73
+
74
+ def test_waitpid
75
+ pid = fork do
76
+ @agent.post_fork
77
+ exit(42)
78
+ end
79
+
80
+ result = @agent.waitpid(pid)
81
+ assert_equal [pid, 42], result
82
+ end
83
+
84
+ def test_read_loop
85
+ i, o = IO.pipe
86
+
87
+ buf = []
88
+ spin do
89
+ buf << :ready
90
+ @agent.read_loop(i) { |d| buf << d }
91
+ buf << :done
92
+ end
93
+
94
+ # writing always causes snoozing
95
+ o << 'foo'
96
+ o << 'bar'
97
+ o.close
98
+
99
+ # read_loop will snooze after every read
100
+ 4.times { snooze }
101
+
102
+ assert_equal [:ready, 'foo', 'bar', :done], buf
103
+ end
104
+
105
+ def test_accept_loop
106
+ server = TCPServer.new('127.0.0.1', 1234)
107
+
108
+ clients = []
109
+ server_fiber = spin do
110
+ @agent.accept_loop(server) { |c| clients << c }
111
+ end
112
+
113
+ c1 = TCPSocket.new('127.0.0.1', 1234)
114
+ snooze
115
+
116
+ assert_equal 1, clients.size
117
+
118
+ c2 = TCPSocket.new('127.0.0.1', 1234)
119
+ 2.times { snooze }
120
+
121
+ assert_equal 2, clients.size
122
+
123
+ ensure
124
+ c1&.close
125
+ c2&.close
126
+ server_fiber.stop
127
+ snooze
128
+ server&.close
129
+ end
130
+ end
@@ -2,26 +2,29 @@
2
2
 
3
3
  require_relative 'helper'
4
4
 
5
- class AsyncTest < MiniTest::Test
6
- def test_that_async_watcher_receives_signal_across_threads
5
+ class EventTest < MiniTest::Test
6
+ def test_that_event_receives_signal_across_threads
7
7
  count = 0
8
- a = Gyro::Async.new
8
+ a = Polyphony::Event.new
9
9
  spin {
10
10
  a.await
11
11
  count += 1
12
12
  }
13
13
  snooze
14
- Thread.new do
14
+ t = Thread.new do
15
15
  orig_sleep 0.001
16
16
  a.signal
17
17
  end
18
18
  suspend
19
19
  assert_equal 1, count
20
+ ensure
21
+ t&.kill
22
+ t&.join
20
23
  end
21
24
 
22
- def test_that_async_watcher_coalesces_signals
25
+ def test_that_event_coalesces_signals
23
26
  count = 0
24
- a = Gyro::Async.new
27
+ a = Polyphony::Event.new
25
28
 
26
29
  coproc = spin {
27
30
  loop {
@@ -31,12 +34,15 @@ class AsyncTest < MiniTest::Test
31
34
  }
32
35
  }
33
36
  snooze
34
- Thread.new do
37
+ t = Thread.new do
35
38
  orig_sleep 0.001
36
39
  3.times { a.signal }
37
40
  end
38
41
 
39
42
  coproc.await
40
43
  assert_equal 1, count
44
+ ensure
45
+ t&.kill
46
+ t&.join
41
47
  end
42
48
  end
@@ -74,7 +74,7 @@ class KernelTest < MiniTest::Test
74
74
  $stderr.rewind
75
75
  $stderr = prev_stderr
76
76
 
77
- assert_nil data
77
+ assert_equal '', data
78
78
  assert_equal "error\n", err_io.read
79
79
  ensure
80
80
  $stderr = prev_stderr
@@ -93,6 +93,28 @@ class KernelTest < MiniTest::Test
93
93
  $stdin = prev_stdin
94
94
  end
95
95
 
96
+ def test_multiline_gets
97
+ prev_stdin = $stdin
98
+ i, o = IO.pipe
99
+ $stdin = i
100
+
101
+ spin do
102
+ o << "hello\n"
103
+ o << "world\n"
104
+ o << "nice\n"
105
+ o << "to\n"
106
+ o << "meet\n"
107
+ o << "you\n"
108
+ end
109
+
110
+ s = +''
111
+ 6.times { s << gets }
112
+
113
+ assert_equal "hello\nworld\nnice\nto\nmeet\nyou\n", s
114
+ ensure
115
+ $stdin = prev_stdin
116
+ end
117
+
96
118
  def test_gets_from_argv
97
119
  prev_stdin = $stdin
98
120
 
@@ -103,8 +125,7 @@ class KernelTest < MiniTest::Test
103
125
  count = contents.size
104
126
 
105
127
  buffer = []
106
-
107
- (count * 2).times { buffer << gets }
128
+ (count * 2).times { |i| s = gets; buffer << s }
108
129
  assert_equal contents * 2, buffer
109
130
 
110
131
  i, o = IO.pipe
@@ -172,4 +193,4 @@ class TimeoutTest < MiniTest::Test
172
193
  assert_kind_of MyTimeout, e
173
194
  assert_equal 'foo', e.message
174
195
  end
175
- end
196
+ end
@@ -110,7 +110,7 @@ class FiberTest < MiniTest::Test
110
110
  def test_cross_thread_schedule
111
111
  buffer = []
112
112
  worker_fiber = nil
113
- async = Gyro::Async.new
113
+ async = Polyphony::Event.new
114
114
  worker = Thread.new do
115
115
  worker_fiber = Fiber.current
116
116
  async.signal
@@ -125,10 +125,11 @@ class FiberTest < MiniTest::Test
125
125
  assert_equal [:foo], buffer
126
126
  ensure
127
127
  worker&.kill
128
+ worker&.join
128
129
  end
129
130
 
130
131
  def test_ev_loop_anti_starve_mechanism
131
- async = Gyro::Async.new
132
+ async = Polyphony::Event.new
132
133
  t = Thread.new do
133
134
  f = spin_loop { snooze }
134
135
  sleep 0.001
@@ -139,7 +140,8 @@ class FiberTest < MiniTest::Test
139
140
 
140
141
  assert_equal :foo, result
141
142
  ensure
142
- t.kill if t.alive?
143
+ t&.kill
144
+ t&.join
143
145
  end
144
146
 
145
147
  def test_tag
@@ -350,8 +352,7 @@ class FiberTest < MiniTest::Test
350
352
  result = []
351
353
  f = Fiber.current.spin do
352
354
  result << :start
353
- t = Gyro::Timer.new(1, 0)
354
- result << t.await
355
+ result << Thread.current.agent.sleep(1)
355
356
  end
356
357
  snooze
357
358
  f.interrupt
@@ -624,7 +625,7 @@ class FiberTest < MiniTest::Test
624
625
  end
625
626
  end
626
627
  sleep 0.1
627
- f = spin { Gyro::Child.new(pid).await }
628
+ f = spin { Thread.current.agent.waitpid(pid) }
628
629
  o.close
629
630
  Process.kill('INT', pid)
630
631
  f.await
@@ -646,7 +647,7 @@ class FiberTest < MiniTest::Test
646
647
  end
647
648
  end
648
649
  sleep 0.2
649
- f = spin { Gyro::Child.new(pid).await }
650
+ f = spin { Thread.current.agent.waitpid(pid) }
650
651
  o.close
651
652
  Process.kill('TERM', pid)
652
653
  f.await
@@ -673,7 +674,7 @@ class FiberTest < MiniTest::Test
673
674
  sleep 0.2
674
675
  Process.kill('TERM', pid)
675
676
  end
676
- Gyro::Child.new(pid).await
677
+ Thread.current.agent.waitpid(pid)
677
678
  klass = i.read
678
679
  i.close
679
680
  assert_equal 'Polyphony::Terminate', klass
@@ -707,6 +708,7 @@ class MailboxTest < MiniTest::Test
707
708
  f << i
708
709
  sleep 0
709
710
  end
711
+ sleep 0
710
712
 
711
713
  assert_equal [0, 1, 2], msgs
712
714
  ensure
@@ -717,11 +719,11 @@ class MailboxTest < MiniTest::Test
717
719
  msgs = []
718
720
  f = spin { loop { msgs << receive } }
719
721
 
720
- snooze # allow coproc to start
722
+ snooze # allow f to start
721
723
 
722
724
  3.times { |i| f << i }
723
725
 
724
- sleep 0
726
+ sleep 0.01
725
727
 
726
728
  assert_equal [0, 1, 2], msgs
727
729
  ensure
@@ -743,6 +745,7 @@ class MailboxTest < MiniTest::Test
743
745
  def test_cross_thread_send_receive
744
746
  ping_receive_buffer = []
745
747
  pong_receive_buffer = []
748
+
746
749
  pong = Thread.new do
747
750
  sleep 0.05
748
751
  loop do
@@ -763,9 +766,15 @@ class MailboxTest < MiniTest::Test
763
766
 
764
767
  ping.join
765
768
  pong.kill
769
+ ping = pong = nil
766
770
 
767
771
  assert_equal %w{pong pong pong}, ping_receive_buffer
768
772
  assert_equal %w{ping ping ping}, pong_receive_buffer
773
+ ensure
774
+ pong&.kill
775
+ ping&.kill
776
+ pong&.join
777
+ ping&.join
769
778
  end
770
779
 
771
780
  def test_message_queueing