polyphony 0.19 → 0.20

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +87 -1
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile.lock +17 -6
  6. data/README.md +200 -139
  7. data/Rakefile +4 -4
  8. data/TODO.md +35 -7
  9. data/bin/poly +11 -0
  10. data/docs/getting-started/getting-started.md +1 -1
  11. data/docs/summary.md +3 -0
  12. data/docs/technical-overview/exception-handling.md +94 -0
  13. data/docs/technical-overview/fiber-scheduling.md +99 -0
  14. data/examples/core/cancel.rb +8 -4
  15. data/examples/core/channel_echo.rb +18 -17
  16. data/examples/core/defer.rb +12 -0
  17. data/examples/core/enumerator.rb +4 -4
  18. data/examples/core/fiber_error.rb +9 -0
  19. data/examples/core/fiber_error_with_backtrace.rb +73 -0
  20. data/examples/core/fork.rb +6 -6
  21. data/examples/core/genserver.rb +16 -8
  22. data/examples/core/lock.rb +3 -3
  23. data/examples/core/move_on.rb +4 -3
  24. data/examples/core/move_on_twice.rb +5 -5
  25. data/examples/core/move_on_with_ensure.rb +8 -11
  26. data/examples/core/move_on_with_value.rb +14 -0
  27. data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
  28. data/examples/core/nested_cancel.rb +5 -5
  29. data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
  30. data/examples/core/nested_spin.rb +17 -0
  31. data/examples/core/pingpong.rb +21 -0
  32. data/examples/core/pulse.rb +4 -5
  33. data/examples/core/resource.rb +6 -4
  34. data/examples/core/resource_cancel.rb +6 -9
  35. data/examples/core/resource_delegate.rb +3 -3
  36. data/examples/core/sleep.rb +3 -3
  37. data/examples/core/sleep_spin.rb +19 -0
  38. data/examples/core/snooze.rb +32 -0
  39. data/examples/core/spin.rb +14 -0
  40. data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
  41. data/examples/core/spin_error.rb +17 -0
  42. data/examples/core/spin_error_backtrace.rb +30 -0
  43. data/examples/core/spin_uncaught_error.rb +15 -0
  44. data/examples/core/supervisor.rb +8 -8
  45. data/examples/core/supervisor_with_cancel_scope.rb +7 -7
  46. data/examples/core/supervisor_with_error.rb +8 -8
  47. data/examples/core/supervisor_with_manual_move_on.rb +6 -7
  48. data/examples/core/suspend.rb +13 -0
  49. data/examples/core/thread.rb +1 -1
  50. data/examples/core/thread_cancel.rb +9 -11
  51. data/examples/core/thread_pool.rb +18 -14
  52. data/examples/core/throttle.rb +7 -7
  53. data/examples/core/timeout.rb +3 -3
  54. data/examples/fs/read.rb +7 -9
  55. data/examples/http/config.ru +7 -3
  56. data/examples/http/cuba.ru +22 -0
  57. data/examples/http/happy_eyeballs.rb +6 -4
  58. data/examples/http/http_client.rb +1 -1
  59. data/examples/http/http_get.rb +1 -1
  60. data/examples/http/http_parse_experiment.rb +21 -16
  61. data/examples/http/http_proxy.rb +28 -26
  62. data/examples/http/http_server.rb +10 -10
  63. data/examples/http/http_server_forked.rb +6 -5
  64. data/examples/http/http_server_throttled.rb +3 -3
  65. data/examples/http/http_ws_server.rb +11 -11
  66. data/examples/http/https_raw_client.rb +1 -1
  67. data/examples/http/https_server.rb +8 -8
  68. data/examples/http/https_wss_server.rb +13 -11
  69. data/examples/http/rack_server.rb +2 -2
  70. data/examples/http/rack_server_https.rb +4 -4
  71. data/examples/http/rack_server_https_forked.rb +5 -5
  72. data/examples/http/websocket_secure_server.rb +6 -6
  73. data/examples/http/websocket_server.rb +5 -5
  74. data/examples/interfaces/pg_client.rb +4 -4
  75. data/examples/interfaces/pg_pool.rb +13 -6
  76. data/examples/interfaces/pg_transaction.rb +5 -4
  77. data/examples/interfaces/redis_channels.rb +15 -11
  78. data/examples/interfaces/redis_client.rb +2 -2
  79. data/examples/interfaces/redis_pubsub.rb +2 -1
  80. data/examples/interfaces/redis_pubsub_perf.rb +13 -9
  81. data/examples/io/backticks.rb +11 -0
  82. data/examples/io/cat.rb +4 -5
  83. data/examples/io/echo_client.rb +9 -4
  84. data/examples/io/echo_client_from_stdin.rb +20 -0
  85. data/examples/io/echo_pipe.rb +7 -8
  86. data/examples/io/echo_server.rb +8 -6
  87. data/examples/io/echo_server_with_timeout.rb +13 -10
  88. data/examples/io/echo_stdin.rb +3 -3
  89. data/examples/io/httparty.rb +2 -2
  90. data/examples/io/httparty_multi.rb +8 -4
  91. data/examples/io/httparty_threaded.rb +6 -2
  92. data/examples/io/io_read.rb +2 -2
  93. data/examples/io/irb.rb +16 -4
  94. data/examples/io/net-http.rb +3 -3
  95. data/examples/io/open.rb +17 -0
  96. data/examples/io/system.rb +3 -3
  97. data/examples/io/tcpserver.rb +15 -0
  98. data/examples/io/tcpsocket.rb +6 -5
  99. data/examples/performance/multi_snooze.rb +29 -0
  100. data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
  101. data/examples/performance/snooze_raw.rb +39 -0
  102. data/ext/gyro/async.c +165 -0
  103. data/ext/gyro/child.c +167 -0
  104. data/ext/{ev → gyro}/extconf.rb +4 -3
  105. data/ext/gyro/gyro.c +316 -0
  106. data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
  107. data/ext/gyro/gyro_ext.c +23 -0
  108. data/ext/{ev → gyro}/io.c +65 -57
  109. data/ext/{ev → gyro}/libev.h +0 -0
  110. data/ext/gyro/signal.c +117 -0
  111. data/ext/{ev → gyro}/socket.c +61 -6
  112. data/ext/gyro/timer.c +199 -0
  113. data/ext/libev/Changes +35 -0
  114. data/ext/libev/README +2 -1
  115. data/ext/libev/ev.c +213 -151
  116. data/ext/libev/ev.h +95 -88
  117. data/ext/libev/ev_epoll.c +26 -15
  118. data/ext/libev/ev_kqueue.c +11 -5
  119. data/ext/libev/ev_linuxaio.c +642 -0
  120. data/ext/libev/ev_poll.c +13 -8
  121. data/ext/libev/ev_port.c +5 -2
  122. data/ext/libev/ev_vars.h +14 -3
  123. data/ext/libev/ev_wrap.h +16 -0
  124. data/lib/ev_ext.bundle +0 -0
  125. data/lib/polyphony.rb +46 -50
  126. data/lib/polyphony/auto_run.rb +12 -0
  127. data/lib/polyphony/core/cancel_scope.rb +11 -7
  128. data/lib/polyphony/core/channel.rb +16 -9
  129. data/lib/polyphony/core/coprocess.rb +101 -51
  130. data/lib/polyphony/core/exceptions.rb +14 -12
  131. data/lib/polyphony/core/resource_pool.rb +21 -8
  132. data/lib/polyphony/core/supervisor.rb +10 -5
  133. data/lib/polyphony/core/sync.rb +7 -6
  134. data/lib/polyphony/core/thread.rb +4 -4
  135. data/lib/polyphony/core/thread_pool.rb +4 -4
  136. data/lib/polyphony/core/throttler.rb +6 -4
  137. data/lib/polyphony/extensions/core.rb +253 -0
  138. data/lib/polyphony/extensions/io.rb +28 -16
  139. data/lib/polyphony/extensions/openssl.rb +2 -1
  140. data/lib/polyphony/extensions/socket.rb +47 -52
  141. data/lib/polyphony/http.rb +4 -3
  142. data/lib/polyphony/http/agent.rb +68 -57
  143. data/lib/polyphony/http/server.rb +5 -5
  144. data/lib/polyphony/http/server/http1.rb +268 -0
  145. data/lib/polyphony/http/server/http2.rb +62 -0
  146. data/lib/polyphony/http/server/http2_stream.rb +104 -0
  147. data/lib/polyphony/http/server/rack.rb +64 -0
  148. data/lib/polyphony/http/server/request.rb +119 -0
  149. data/lib/polyphony/net.rb +26 -15
  150. data/lib/polyphony/postgres.rb +17 -13
  151. data/lib/polyphony/redis.rb +16 -15
  152. data/lib/polyphony/version.rb +1 -1
  153. data/lib/polyphony/websocket.rb +11 -4
  154. data/polyphony.gemspec +13 -9
  155. data/test/eg.rb +27 -0
  156. data/test/helper.rb +25 -0
  157. data/test/run.rb +5 -0
  158. data/test/test_async.rb +33 -0
  159. data/test/test_coprocess.rb +239 -77
  160. data/test/test_core.rb +95 -61
  161. data/test/test_gyro.rb +148 -0
  162. data/test/test_http_server.rb +313 -0
  163. data/test/test_io.rb +79 -27
  164. data/test/test_kernel.rb +22 -12
  165. data/test/test_signal.rb +36 -0
  166. data/test/test_timer.rb +24 -0
  167. metadata +89 -33
  168. data/examples/core/nested_async.rb +0 -17
  169. data/examples/core/next_tick.rb +0 -12
  170. data/examples/core/sleep_spawn.rb +0 -19
  171. data/examples/core/spawn.rb +0 -14
  172. data/examples/core/spawn_error.rb +0 -28
  173. data/examples/performance/perf_multi_snooze.rb +0 -21
  174. data/ext/ev/async.c +0 -168
  175. data/ext/ev/child.c +0 -169
  176. data/ext/ev/ev_ext.c +0 -23
  177. data/ext/ev/ev_module.c +0 -242
  178. data/ext/ev/signal.c +0 -119
  179. data/ext/ev/timer.c +0 -197
  180. data/lib/polyphony/core/fiber_pool.rb +0 -98
  181. data/lib/polyphony/extensions/kernel.rb +0 -169
  182. data/lib/polyphony/http/http1_adapter.rb +0 -254
  183. data/lib/polyphony/http/http2_adapter.rb +0 -157
  184. data/lib/polyphony/http/rack.rb +0 -25
  185. data/lib/polyphony/http/request.rb +0 -66
  186. data/test/test_ev.rb +0 -110
data/polyphony.gemspec CHANGED
@@ -14,20 +14,24 @@ Gem::Specification.new do |s|
14
14
  }
15
15
  s.rdoc_options = ["--title", "polyphony", "--main", "README.md"]
16
16
  s.extra_rdoc_files = ["README.md"]
17
- s.extensions = ["ext/ev/extconf.rb"]
17
+ s.extensions = ["ext/gyro/extconf.rb"]
18
18
  s.require_paths = ["lib"]
19
19
 
20
+ s.executables = ['poly']
21
+
20
22
  s.add_runtime_dependency 'modulation', '~>0.25'
21
23
 
22
24
  s.add_runtime_dependency 'http_parser.rb', '0.6.0'
23
25
  s.add_runtime_dependency 'http-2', '0.10.0'
26
+ s.add_runtime_dependency 'rack'
24
27
 
25
- s.add_development_dependency 'hiredis', '0.6.3'
26
- s.add_development_dependency 'httparty', '0.17.0'
27
- s.add_development_dependency 'localhost', '1.1.4'
28
- s.add_development_dependency 'minitest', '5.11.3'
29
- s.add_development_dependency 'pg', '1.1.3'
30
- s.add_development_dependency 'rake-compiler', '1.0.5'
31
- s.add_development_dependency 'redis', '4.1.0'
32
- s.add_development_dependency 'websocket', '1.2.8'
28
+ s.add_development_dependency 'hiredis', '0.6.3'
29
+ s.add_development_dependency 'httparty', '0.17.0'
30
+ s.add_development_dependency 'localhost', '1.1.4'
31
+ s.add_development_dependency 'minitest', '5.11.3'
32
+ s.add_development_dependency 'minitest-reporters', '1.4.2'
33
+ s.add_development_dependency 'pg', '1.1.3'
34
+ s.add_development_dependency 'rake-compiler', '1.0.5'
35
+ s.add_development_dependency 'redis', '4.1.0'
36
+ s.add_development_dependency 'websocket', '1.2.8'
33
37
  end
data/test/eg.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kernel
4
+ RE_CONST = /^[A-Z]/.freeze
5
+ RE_ATTR = /^@(.+)$/.freeze
6
+
7
+ def eg(hash)
8
+ Module.new.tap do |m|
9
+ s = m.singleton_class
10
+ hash.each do |k, v|
11
+ case k
12
+ when RE_CONST
13
+ m.const_set(k, v)
14
+ when RE_ATTR
15
+ m.instance_variable_set(k, v)
16
+ else
17
+ block = if v.respond_to?(:to_proc)
18
+ proc { |*args| instance_exec(*args, &v) }
19
+ else
20
+ proc { v }
21
+ end
22
+ s.define_method(k, &block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'minitest/autorun'
6
+ require 'minitest/reporters'
7
+
8
+ require 'polyphony'
9
+ require 'fileutils'
10
+
11
+ require_relative './eg'
12
+
13
+ ::Exception.__disable_sanitized_backtrace__ = true
14
+
15
+ Minitest::Reporters.use! [
16
+ Minitest::Reporters::SpecReporter.new
17
+ ]
18
+
19
+ class MiniTest::Test
20
+ def teardown
21
+ # wait for reactor loop to finish running
22
+ suspend
23
+ Polyphony.reset!
24
+ end
25
+ end
data/test/run.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir.glob("#{__dir__}/test_*.rb").each do |path|
4
+ require(path)
5
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class AsyncTest < MiniTest::Test
6
+ def test_that_async_watcher_receives_signal_across_threads
7
+ count = 0
8
+ a = Gyro::Async.new do
9
+ count += 1
10
+ a.stop
11
+ end
12
+ Thread.new do
13
+ sync_sleep 0.001
14
+ a.signal!
15
+ end
16
+ suspend
17
+ assert_equal(1, count)
18
+ end
19
+
20
+ def test_that_async_watcher_coalesces_signals
21
+ count = 0
22
+ a = Gyro::Async.new do
23
+ count += 1
24
+ Gyro::Timer.new(0.01, 0).start { a.stop }
25
+ end
26
+ Thread.new do
27
+ sync_sleep 0.001
28
+ 3.times { a.signal! }
29
+ end
30
+ suspend
31
+ assert_equal(1, count)
32
+ end
33
+ end
@@ -1,13 +1,11 @@
1
- require 'minitest/autorun'
2
- require 'bundler/setup'
3
- require 'polyphony'
1
+ # frozen_string_literal: true
4
2
 
5
- class CoprocessTest < MiniTest::Test
6
- def setup
7
- EV.rerun
8
- end
3
+ require_relative 'helper'
4
+
5
+ STDOUT.sync = true
9
6
 
10
- def test_that_main_fiber_has_associated_coprocess
7
+ class CoprocessTest < MiniTest::Test
8
+ def test_that_root_fiber_has_associated_coprocess
11
9
  assert_equal(Fiber.current, Polyphony::Coprocess.current.fiber)
12
10
  assert_equal(Polyphony::Coprocess.current, Fiber.current.coprocess)
13
11
  end
@@ -18,24 +16,35 @@ class CoprocessTest < MiniTest::Test
18
16
  assert_nil(result)
19
17
  coproc.await
20
18
  assert_equal(42, result)
19
+ ensure
20
+ coproc&.stop
21
21
  end
22
22
 
23
23
  def test_that_new_coprocess_runs_on_different_fiber
24
24
  coproc = Polyphony::Coprocess.new { Fiber.current }
25
25
  fiber = coproc.await
26
26
  assert(fiber != Fiber.current)
27
+ ensure
28
+ coproc&.stop
27
29
  end
28
30
 
29
31
  def test_that_await_blocks_until_coprocess_is_done
30
32
  result = nil
31
- coproc = Polyphony::Coprocess.new { sleep 0.001; result = 42 }
33
+ coproc = Polyphony::Coprocess.new do
34
+ snooze
35
+ result = 42
36
+ end
32
37
  coproc.await
33
38
  assert_equal(42, result)
39
+ ensure
40
+ coproc&.stop
34
41
  end
35
42
 
36
43
  def test_that_await_returns_the_coprocess_return_value
37
- coproc = Polyphony::Coprocess.new { [:foo, :bar] }
38
- assert_equal([:foo, :bar], coproc.await)
44
+ coproc = Polyphony::Coprocess.new { %i[foo bar] }
45
+ assert_equal(%i[foo bar], coproc.await)
46
+ ensure
47
+ coproc&.stop
39
48
  end
40
49
 
41
50
  def test_that_await_raises_error_raised_by_coprocess
@@ -43,162 +52,315 @@ class CoprocessTest < MiniTest::Test
43
52
  coproc = Polyphony::Coprocess.new { raise 'foo' }
44
53
  begin
45
54
  result = coproc.await
46
- rescue => e
55
+ rescue Exception => e
47
56
  result = { error: e }
48
57
  end
49
58
  assert_kind_of(Hash, result)
50
59
  assert_kind_of(RuntimeError, result[:error])
60
+ ensure
61
+ coproc&.stop
51
62
  end
52
63
 
53
64
  def test_that_running_coprocess_can_be_cancelled
54
65
  result = []
55
- coproc = Polyphony::Coprocess.new {
66
+ error = nil
67
+ coproc = Polyphony::Coprocess.new do
56
68
  result << 1
57
- sleep 0.002
69
+ 2.times { snooze }
58
70
  result << 2
59
- }
60
- EV::Timer.new(0.001, 0).start { coproc.cancel! }
71
+ end.run
72
+ defer { coproc.cancel! }
73
+ assert_equal(0, result.size)
61
74
  begin
62
75
  coproc.await
63
- rescue Exception => e
64
- result << e
76
+ rescue Polyphony::Cancel => e
77
+ error = e
65
78
  end
66
- assert_equal(2, result.size)
79
+ assert_equal(1, result.size)
67
80
  assert_equal(1, result[0])
68
- assert_kind_of(Polyphony::Cancel, result[1])
81
+ assert_kind_of(Polyphony::Cancel, error)
82
+ ensure
83
+ coproc&.stop
69
84
  end
70
85
 
71
86
  def test_that_running_coprocess_can_be_interrupted
72
87
  # that is, stopped without exception
73
88
  result = []
74
- coproc = Polyphony::Coprocess.new {
89
+ coproc = Polyphony::Coprocess.new do
75
90
  result << 1
76
- sleep 0.002
91
+ 2.times { snooze }
77
92
  result << 2
78
93
  3
79
- }
80
- EV::Timer.new(0.001, 0).start { coproc.stop(42) }
94
+ end.run
95
+ defer { coproc.stop(42) }
81
96
 
82
97
  await_result = coproc.await
83
98
  assert_equal(1, result.size)
84
99
  assert_equal(42, await_result)
100
+ ensure
101
+ coproc&.stop
85
102
  end
86
103
 
87
104
  def test_that_coprocess_can_be_awaited
88
105
  result = nil
89
- spin do
90
- coprocess = Polyphony::Coprocess.new { sleep(0.001); 42 }
91
- result = coprocess.await
106
+ cp2 = nil
107
+ cp1 = spin do
108
+ cp2 = Polyphony::Coprocess.new do
109
+ snooze
110
+ 42
111
+ end
112
+ result = cp2.await
92
113
  end
93
114
  suspend
94
115
  assert_equal(42, result)
116
+ ensure
117
+ cp1&.stop
118
+ cp2&.stop
95
119
  end
96
120
 
97
121
  def test_that_coprocess_can_be_stopped
98
122
  result = nil
99
- coprocess = spin do
100
- sleep(0.001)
123
+ coproc = spin do
124
+ snooze
101
125
  result = 42
102
126
  end
103
- EV.next_tick { coprocess.interrupt }
127
+ defer { coproc.interrupt }
104
128
  suspend
105
129
  assert_nil(result)
130
+ ensure
131
+ coproc&.stop
106
132
  end
107
133
 
108
134
  def test_that_coprocess_can_be_cancelled
109
135
  result = nil
110
- coprocess = spin do
111
- sleep(0.001)
136
+ coproc = spin do
137
+ snooze
112
138
  result = 42
113
139
  rescue Polyphony::Cancel => e
114
140
  result = e
115
141
  end
116
- EV.next_tick { coprocess.cancel! }
142
+ defer { coproc.cancel! }
117
143
 
118
144
  suspend
119
145
 
120
146
  assert_kind_of(Polyphony::Cancel, result)
121
- assert_kind_of(Polyphony::Cancel, coprocess.result)
122
- assert_nil(coprocess.running?)
147
+ assert_kind_of(Polyphony::Cancel, coproc.result)
148
+ assert_nil(coproc.alive?)
149
+ ensure
150
+ coproc&.stop
123
151
  end
124
152
 
125
153
  def test_that_inner_coprocess_can_be_interrupted
126
154
  result = nil
127
- coprocess2 = nil
128
- coprocess = spin do
129
- coprocess2 = spin do
130
- sleep(0.001)
155
+ cp2 = nil
156
+ cp1 = spin do
157
+ cp2 = spin do
158
+ snooze
131
159
  result = 42
132
160
  end
133
- coprocess2.await
161
+ cp2.await
134
162
  result && result += 1
135
163
  end
136
- EV.next_tick { coprocess.interrupt }
164
+ defer { cp1.interrupt }
137
165
  suspend
138
166
  assert_nil(result)
139
- assert_nil(coprocess.running?)
140
- assert_nil(coprocess2.running?)
167
+ assert_nil(cp1.alive?)
168
+ assert_nil(cp2.alive?)
169
+ ensure
170
+ cp1&.stop
171
+ cp2&.stop
141
172
  end
142
173
 
143
174
  def test_that_inner_coprocess_can_interrupt_outer_coprocess
144
- result, coprocess2 = nil
145
-
146
- coprocess = spin do
147
- coprocess2 = spin do
148
- EV.next_tick { coprocess.interrupt }
149
- sleep(0.001)
175
+ result, cp2 = nil
176
+
177
+ cp1 = spin do
178
+ cp2 = spin do
179
+ defer { cp1.interrupt }
180
+ snooze
181
+ snooze
150
182
  result = 42
151
183
  end
152
- coprocess2.await
184
+ cp2.await
153
185
  result && result += 1
154
186
  end
155
-
187
+
156
188
  suspend
157
-
189
+
158
190
  assert_nil(result)
159
- assert_nil(coprocess.running?)
160
- assert_nil(coprocess2.running?)
191
+ assert_nil(cp1.alive?)
192
+ assert_nil(cp2.alive?)
193
+ ensure
194
+ cp1&.stop
195
+ cp2&.stop
196
+ end
197
+
198
+ def test_alive?
199
+ counter = 0
200
+ coproc = spin do
201
+ 3.times do
202
+ snooze
203
+ counter += 1
204
+ end
205
+ end
206
+
207
+ assert(coproc.alive?)
208
+ snooze
209
+ assert(coproc.alive?)
210
+ snooze while counter < 3
211
+ assert(!coproc.alive?)
212
+ ensure
213
+ coproc&.stop
214
+ end
215
+
216
+ def test_coprocess_exception_propagation
217
+ # error is propagated to calling coprocess
218
+ raised_error = nil
219
+ spin do
220
+ spin do
221
+ raise 'foo'
222
+ end
223
+ snooze # allow nested coprocess to run before finishing
224
+ end
225
+ suspend
226
+ rescue Exception => e
227
+ raised_error = e
228
+ ensure
229
+ assert(raised_error)
230
+ assert_equal('foo', raised_error.message)
161
231
  end
162
232
  end
163
233
 
164
- class MailboxTest < MiniTest::Test
165
- def setup
166
- EV.rerun
234
+ def test_exception_propagation_for_orphan_fiber
235
+ raised_error = nil
236
+ spin do
237
+ spin do
238
+ snooze
239
+ raise 'bar'
240
+ end
167
241
  end
242
+ suspend
243
+ rescue Exception => e
244
+ raised_error = e
245
+ ensure
246
+ assert(raised_error)
247
+ assert_equal('bar', raised_error.message)
248
+ end
168
249
 
250
+ class MailboxTest < MiniTest::Test
169
251
  def test_that_coprocess_can_receive_messages
170
252
  msgs = []
171
- coprocess = spin {
172
- loop {
173
- msgs << receive
174
- }
175
- }
253
+ coproc = spin { loop { msgs << receive } }
176
254
 
177
- EV.snooze # allow coproc to start
178
-
179
- 3.times { |i| coprocess << i; EV.snooze }
255
+ snooze # allow coproc to start
256
+
257
+ 3.times do |i|
258
+ coproc << i
259
+ snooze
260
+ end
180
261
 
181
262
  assert_equal([0, 1, 2], msgs)
182
263
  ensure
183
- coprocess.stop
264
+ coproc&.stop
184
265
  end
185
266
 
186
- def test_that_multiple_messages_sent_at_once_arrive
267
+ def test_that_multiple_messages_sent_at_once_arrive_in_order
187
268
  msgs = []
188
- coprocess = spin {
189
- loop {
190
- msgs << receive
191
- }
192
- }
269
+ coproc = spin { loop { msgs << receive } }
270
+
271
+ snooze # allow coproc to start
193
272
 
194
- EV.snooze # allow coproc to start
195
-
196
- 3.times { |i| coprocess << i }
273
+ 3.times { |i| coproc << i }
197
274
 
198
- EV.snooze
275
+ snooze
199
276
 
200
277
  assert_equal([0, 1, 2], msgs)
201
278
  ensure
202
- coprocess.stop
279
+ coproc&.stop
280
+ end
281
+
282
+ def test_when_done
283
+ flag = nil
284
+ values = []
285
+ coproc = spin do
286
+ snooze until flag
287
+ end
288
+ coproc.when_done { values << 42 }
289
+
290
+ snooze
291
+ assert values.empty?
292
+ snooze
293
+ flag = true
294
+ assert values.empty?
295
+ assert coproc.alive?
296
+
297
+ snooze
298
+ assert_equal [42], values
299
+ assert !coproc.alive?
300
+ end
301
+
302
+ def test_resume
303
+ values = []
304
+ coproc = spin do
305
+ values << 1
306
+ x = suspend
307
+ values << x
308
+ suspend
309
+ values << 3
310
+ end
311
+ snooze
312
+ assert_equal [1], values
313
+
314
+ coproc.resume 2
315
+ assert_equal [1, 2], values
316
+
317
+ coproc.resume
318
+ assert_equal [1, 2, 3], values
319
+
320
+ assert !coproc.alive?
321
+ end
322
+
323
+ def test_interrupt
324
+ coproc = spin do
325
+ sleep 1
326
+ :foo
327
+ end
328
+
329
+ snooze
330
+ assert coproc.alive?
331
+
332
+ coproc.interrupt :bar
333
+ assert !coproc.alive?
334
+
335
+ assert_equal :bar, coproc.result
336
+ end
337
+
338
+ def test_cancel
339
+ error = nil
340
+ coproc = spin do
341
+ sleep 1
342
+ :foo
343
+ end
344
+
345
+ snooze
346
+ coproc.cancel!
347
+ rescue Polyphony::Cancel => e
348
+ # cancel error should bubble up
349
+ error = e
350
+ ensure
351
+ assert error
352
+ assert !coproc.alive?
353
+ end
354
+
355
+ def test_current
356
+ assert_equal Fiber.root.coprocess, Polyphony::Coprocess.current
357
+
358
+ value = nil
359
+ coproc = spin do
360
+ value = :ok if Polyphony::Coprocess.current == coproc
361
+ end
362
+
363
+ snooze
364
+ assert_equal :ok, value
203
365
  end
204
366
  end