polyphony 0.19 → 0.20

Sign up to get free protection for your applications and to get access to all the features.
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