polyphony 0.15 → 0.16
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +11 -3
- data/TODO.md +25 -14
- data/docs/getting-started/getting-started.md +1 -1
- data/docs/getting-started/tutorial.md +2 -0
- data/examples/core/cancel.rb +2 -3
- data/examples/core/channel_echo.rb +2 -3
- data/examples/core/enumerator.rb +2 -3
- data/examples/core/fork.rb +2 -2
- data/examples/core/genserver.rb +2 -3
- data/examples/core/lock.rb +2 -3
- data/examples/core/move_on.rb +2 -3
- data/examples/core/move_on_twice.rb +2 -3
- data/examples/core/move_on_with_ensure.rb +2 -3
- data/examples/core/{multiple_async.rb → multiple_spawn.rb} +2 -3
- data/examples/core/nested_async.rb +2 -3
- data/examples/core/nested_cancel.rb +2 -3
- data/examples/core/{nested_multiple_async.rb → nested_multiple_spawn.rb} +2 -3
- data/examples/core/next_tick.rb +4 -5
- data/examples/core/pulse.rb +2 -3
- data/examples/core/resource.rb +2 -3
- data/examples/core/resource_cancel.rb +2 -3
- data/examples/core/resource_delegate.rb +2 -3
- data/examples/core/sleep.rb +2 -3
- data/examples/core/sleep_spawn.rb +19 -0
- data/examples/core/spawn.rb +2 -3
- data/examples/core/spawn_cancel.rb +2 -3
- data/examples/core/spawn_error.rb +2 -2
- data/examples/core/supervisor.rb +2 -3
- data/examples/core/supervisor_with_cancel_scope.rb +2 -3
- data/examples/core/supervisor_with_error.rb +2 -3
- data/examples/core/supervisor_with_manual_move_on.rb +2 -3
- data/examples/core/thread.rb +3 -6
- data/examples/core/thread_cancel.rb +2 -5
- data/examples/core/thread_pool.rb +3 -6
- data/examples/core/throttle.rb +2 -3
- data/examples/fs/read.rb +22 -19
- data/examples/http/happy_eyeballs.rb +2 -2
- data/examples/http/http_client.rb +5 -7
- data/examples/http/http_server.rb +2 -3
- data/examples/http/http_server_forked.rb +2 -3
- data/examples/http/http_server_throttled.rb +2 -3
- data/examples/http/http_ws_server.rb +4 -4
- data/examples/http/https_raw_client.rb +4 -5
- data/examples/http/https_server.rb +2 -3
- data/examples/http/https_wss_server.rb +2 -3
- data/examples/http/rack_server.rb +2 -4
- data/examples/http/rack_server_https.rb +2 -3
- data/examples/http/rack_server_https_forked.rb +2 -3
- data/examples/http/websocket_secure_server.rb +2 -3
- data/examples/http/websocket_server.rb +2 -3
- data/examples/interfaces/pg_client.rb +2 -4
- data/examples/interfaces/pg_pool.rb +4 -6
- data/examples/interfaces/{pg_query.rb → pg_transaction.rb} +2 -4
- data/examples/interfaces/redis_channels.rb +2 -4
- data/examples/interfaces/redis_client.rb +2 -4
- data/examples/interfaces/redis_pubsub.rb +2 -4
- data/examples/interfaces/redis_pubsub_perf.rb +2 -4
- data/examples/io/echo_client.rb +4 -5
- data/examples/io/echo_server.rb +2 -2
- data/examples/io/echo_server_with_timeout.rb +3 -5
- data/examples/io/echo_stdin.rb +3 -4
- data/examples/performance/perf_multi_snooze.rb +2 -2
- data/examples/performance/perf_snooze.rb +2 -2
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +4 -5
- data/ext/ev/ev_module.c +3 -2
- data/lib/polyphony/{resource_pool.rb → core/resource_pool.rb} +0 -0
- data/lib/polyphony/core/supervisor.rb +8 -8
- data/lib/polyphony/extensions/{ssl.rb → openssl.rb} +0 -0
- data/lib/polyphony/http/agent.rb +1 -1
- data/lib/polyphony/http.rb +12 -4
- data/lib/polyphony/net.rb +8 -11
- data/lib/polyphony/{extensions/postgres.rb → postgres.rb} +1 -4
- data/lib/polyphony/{extensions/redis.rb → redis.rb} +3 -6
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony/websocket.rb +1 -1
- data/lib/polyphony.rb +57 -21
- data/polyphony.gemspec +5 -4
- data/test/test_coprocess.rb +88 -15
- data/test/test_core.rb +142 -232
- data/test/test_ev.rb +88 -95
- data/test/test_io.rb +35 -41
- metadata +68 -16
- data/examples/core/sleep2.rb +0 -13
- data/examples/streams/lines.rb +0 -27
- data/examples/streams/stdio.rb +0 -18
- data/lib/polyphony/core.rb +0 -45
- data/lib/polyphony/server_task.rb +0 -18
data/lib/polyphony.rb
CHANGED
@@ -4,37 +4,73 @@ require 'modulation/gem'
|
|
4
4
|
|
5
5
|
export_default :Polyphony
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
import('polyphony/extensions/
|
11
|
-
import('polyphony/extensions/ssl')
|
7
|
+
require 'fiber'
|
8
|
+
require_relative './ev_ext'
|
9
|
+
import('./polyphony/extensions/kernel')
|
10
|
+
import('./polyphony/extensions/io')
|
12
11
|
|
13
12
|
module Polyphony
|
14
|
-
|
15
|
-
|
13
|
+
exceptions = import('./polyphony/core/exceptions')
|
14
|
+
Cancel = exceptions::Cancel
|
15
|
+
MoveOn = exceptions::MoveOn
|
16
|
+
|
17
|
+
Coprocess = import('./polyphony/core/coprocess')
|
18
|
+
FiberPool = import('./polyphony/core/fiber_pool')
|
19
|
+
Net = import('./polyphony/net')
|
16
20
|
|
17
|
-
Net = import('./polyphony/net')
|
18
21
|
|
22
|
+
def self.trap(sig, ref = false, &callback)
|
23
|
+
sig = Signal.list[sig.to_s.upcase] if sig.is_a?(Symbol)
|
24
|
+
watcher = EV::Signal.new(sig, &callback)
|
25
|
+
EV.unref unless ref
|
26
|
+
watcher
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.fork(&block)
|
30
|
+
EV.break
|
31
|
+
pid = Kernel.fork do
|
32
|
+
FiberPool.reset!
|
33
|
+
EV.post_fork
|
34
|
+
Fiber.current.coprocess = Coprocess.new(Fiber.current)
|
35
|
+
|
36
|
+
block.()
|
37
|
+
|
38
|
+
# We cannot simply depend on the at_exit block (see below) to yield to the
|
39
|
+
# reactor fiber. Doing that will raise a FiberError complaining: "fiber
|
40
|
+
# called across stack rewinding barrier". Apparently this is a bug in
|
41
|
+
# Ruby, so the workaround is to yield just before exiting.
|
42
|
+
suspend
|
43
|
+
end
|
44
|
+
EV.restart
|
45
|
+
pid
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.debug
|
49
|
+
@debug
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.debug=(value)
|
53
|
+
@debug = value
|
54
|
+
end
|
55
|
+
|
19
56
|
auto_import(
|
57
|
+
CancelScope: './polyphony/core/cancel_scope',
|
20
58
|
Channel: './polyphony/core/channel',
|
21
|
-
Coprocess: './polyphony/core/coprocess',
|
59
|
+
# Coprocess: './polyphony/core/coprocess',
|
60
|
+
FS: './polyphony/fs',
|
61
|
+
# Net: './polyphony/net',
|
62
|
+
ResourcePool: './polyphony/core/resource_pool',
|
63
|
+
Supervisor: './polyphony/core/supervisor',
|
22
64
|
Sync: './polyphony/core/sync',
|
23
65
|
Thread: './polyphony/core/thread',
|
24
66
|
ThreadPool: './polyphony/core/thread_pool',
|
25
|
-
|
26
|
-
FS: './polyphony/fs',
|
27
|
-
# Net: './polyphony/net',
|
28
|
-
ResourcePool: './polyphony/resource_pool',
|
29
|
-
Supervisor: './polyphony/supervisor',
|
30
67
|
Websocket: './polyphony/websocket'
|
31
68
|
)
|
69
|
+
end
|
32
70
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
)
|
39
|
-
end
|
71
|
+
at_exit do
|
72
|
+
# in most cases, by the main fiber is done there are still pending or other
|
73
|
+
# or asynchronous operations going on. If the reactor loop is not done, we
|
74
|
+
# suspend the root fiber until it is done
|
75
|
+
suspend if $__reactor_fiber__&.alive?
|
40
76
|
end
|
data/polyphony.gemspec
CHANGED
@@ -17,15 +17,16 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.extensions = ["ext/ev/extconf.rb"]
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.add_runtime_dependency 'modulation', '0.
|
20
|
+
s.add_runtime_dependency 'modulation', '0.24'
|
21
21
|
|
22
22
|
s.add_runtime_dependency 'http_parser.rb', '0.6.0'
|
23
23
|
s.add_runtime_dependency 'http-2', '0.10.0'
|
24
24
|
|
25
|
-
# s.add_runtime_dependency 'hiredis', '0.6.1'
|
26
|
-
# s.add_runtime_dependency 'pg', '1.0.0'
|
27
|
-
|
28
25
|
s.add_development_dependency 'rake-compiler', '1.0.5'
|
29
26
|
s.add_development_dependency 'minitest', '5.11.3'
|
30
27
|
s.add_development_dependency 'localhost', '1.1.4'
|
28
|
+
s.add_development_dependency 'websocket', '1.2.8'
|
29
|
+
s.add_development_dependency 'pg', '1.1.3'
|
30
|
+
s.add_development_dependency 'redis', '4.1.0'
|
31
|
+
s.add_development_dependency 'hiredis', '0.6.3'
|
31
32
|
end
|
data/test/test_coprocess.rb
CHANGED
@@ -1,49 +1,46 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'polyphony'
|
3
4
|
|
4
5
|
class CoprocessTest < MiniTest::Test
|
5
|
-
Core = import('../lib/polyphony/core')
|
6
|
-
Coprocess = import('../lib/polyphony/core/coprocess')
|
7
|
-
Exceptions = import('../lib/polyphony/core/exceptions')
|
8
|
-
|
9
6
|
def setup
|
10
7
|
EV.rerun
|
11
8
|
end
|
12
9
|
|
13
10
|
def test_that_main_fiber_has_associated_coprocess
|
14
|
-
assert_equal(Fiber.current, Coprocess.current.fiber)
|
15
|
-
assert_equal(Coprocess.current, Fiber.current.coprocess)
|
11
|
+
assert_equal(Fiber.current, Polyphony::Coprocess.current.fiber)
|
12
|
+
assert_equal(Polyphony::Coprocess.current, Fiber.current.coprocess)
|
16
13
|
end
|
17
14
|
|
18
15
|
def test_that_new_coprocess_starts_in_suspended_state
|
19
16
|
result = nil
|
20
|
-
coproc = Coprocess.new { result = 42 }
|
17
|
+
coproc = Polyphony::Coprocess.new { result = 42 }
|
21
18
|
assert_nil(result)
|
22
19
|
coproc.await
|
23
20
|
assert_equal(42, result)
|
24
21
|
end
|
25
22
|
|
26
23
|
def test_that_new_coprocess_runs_on_different_fiber
|
27
|
-
coproc = Coprocess.new { Fiber.current }
|
24
|
+
coproc = Polyphony::Coprocess.new { Fiber.current }
|
28
25
|
fiber = coproc.await
|
29
26
|
assert(fiber != Fiber.current)
|
30
27
|
end
|
31
28
|
|
32
29
|
def test_that_await_blocks_until_coprocess_is_done
|
33
30
|
result = nil
|
34
|
-
coproc = Coprocess.new { sleep 0.001; result = 42 }
|
31
|
+
coproc = Polyphony::Coprocess.new { sleep 0.001; result = 42 }
|
35
32
|
coproc.await
|
36
33
|
assert_equal(42, result)
|
37
34
|
end
|
38
35
|
|
39
36
|
def test_that_await_returns_the_coprocess_return_value
|
40
|
-
coproc = Coprocess.new { [:foo, :bar] }
|
37
|
+
coproc = Polyphony::Coprocess.new { [:foo, :bar] }
|
41
38
|
assert_equal([:foo, :bar], coproc.await)
|
42
39
|
end
|
43
40
|
|
44
41
|
def test_that_await_raises_error_raised_by_coprocess
|
45
42
|
result = nil
|
46
|
-
coproc = Coprocess.new { raise 'foo' }
|
43
|
+
coproc = Polyphony::Coprocess.new { raise 'foo' }
|
47
44
|
begin
|
48
45
|
result = coproc.await
|
49
46
|
rescue => e
|
@@ -55,7 +52,7 @@ class CoprocessTest < MiniTest::Test
|
|
55
52
|
|
56
53
|
def test_that_running_coprocess_can_be_cancelled
|
57
54
|
result = []
|
58
|
-
coproc = Coprocess.new {
|
55
|
+
coproc = Polyphony::Coprocess.new {
|
59
56
|
result << 1
|
60
57
|
sleep 0.002
|
61
58
|
result << 2
|
@@ -68,13 +65,13 @@ class CoprocessTest < MiniTest::Test
|
|
68
65
|
end
|
69
66
|
assert_equal(2, result.size)
|
70
67
|
assert_equal(1, result[0])
|
71
|
-
assert_kind_of(
|
68
|
+
assert_kind_of(Polyphony::Cancel, result[1])
|
72
69
|
end
|
73
70
|
|
74
71
|
def test_that_running_coprocess_can_be_interrupted
|
75
72
|
# that is, stopped without exception
|
76
73
|
result = []
|
77
|
-
coproc = Coprocess.new {
|
74
|
+
coproc = Polyphony::Coprocess.new {
|
78
75
|
result << 1
|
79
76
|
sleep 0.002
|
80
77
|
result << 2
|
@@ -86,6 +83,82 @@ class CoprocessTest < MiniTest::Test
|
|
86
83
|
assert_equal(1, result.size)
|
87
84
|
assert_equal(42, await_result)
|
88
85
|
end
|
86
|
+
|
87
|
+
def test_that_coprocess_can_be_awaited
|
88
|
+
result = nil
|
89
|
+
spawn do
|
90
|
+
coprocess = Polyphony::Coprocess.new { sleep(0.001); 42 }
|
91
|
+
result = coprocess.await
|
92
|
+
end
|
93
|
+
suspend
|
94
|
+
assert_equal(42, result)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_that_coprocess_can_be_stopped
|
98
|
+
result = nil
|
99
|
+
coprocess = spawn do
|
100
|
+
sleep(0.001)
|
101
|
+
result = 42
|
102
|
+
end
|
103
|
+
EV.next_tick { coprocess.interrupt }
|
104
|
+
suspend
|
105
|
+
assert_nil(result)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_that_coprocess_can_be_cancelled
|
109
|
+
result = nil
|
110
|
+
coprocess = spawn do
|
111
|
+
sleep(0.001)
|
112
|
+
result = 42
|
113
|
+
rescue Polyphony::Cancel => e
|
114
|
+
result = e
|
115
|
+
end
|
116
|
+
EV.next_tick { coprocess.cancel! }
|
117
|
+
|
118
|
+
suspend
|
119
|
+
|
120
|
+
assert_kind_of(Polyphony::Cancel, result)
|
121
|
+
assert_kind_of(Polyphony::Cancel, coprocess.result)
|
122
|
+
assert_nil(coprocess.running?)
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_that_inner_coprocess_can_be_interrupted
|
126
|
+
result = nil
|
127
|
+
coprocess2 = nil
|
128
|
+
coprocess = spawn do
|
129
|
+
coprocess2 = spawn do
|
130
|
+
sleep(0.001)
|
131
|
+
result = 42
|
132
|
+
end
|
133
|
+
coprocess2.await
|
134
|
+
result && result += 1
|
135
|
+
end
|
136
|
+
EV.next_tick { coprocess.interrupt }
|
137
|
+
suspend
|
138
|
+
assert_nil(result)
|
139
|
+
assert_nil(coprocess.running?)
|
140
|
+
assert_nil(coprocess2.running?)
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_that_inner_coprocess_can_interrupt_outer_coprocess
|
144
|
+
result, coprocess2 = nil
|
145
|
+
|
146
|
+
coprocess = spawn do
|
147
|
+
coprocess2 = spawn do
|
148
|
+
EV.next_tick { coprocess.interrupt }
|
149
|
+
sleep(0.001)
|
150
|
+
result = 42
|
151
|
+
end
|
152
|
+
coprocess2.await
|
153
|
+
result && result += 1
|
154
|
+
end
|
155
|
+
|
156
|
+
suspend
|
157
|
+
|
158
|
+
assert_nil(result)
|
159
|
+
assert_nil(coprocess.running?)
|
160
|
+
assert_nil(coprocess2.running?)
|
161
|
+
end
|
89
162
|
end
|
90
163
|
|
91
164
|
class MailboxTest < MiniTest::Test
|
data/test/test_core.rb
CHANGED
@@ -1,274 +1,184 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'polyphony'
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
Coprocess = import('../lib/polyphony/core/coprocess')
|
8
|
-
Exceptions = import('../lib/polyphony/core/exceptions')
|
9
|
-
Supervisor = import('../lib/polyphony/core/supervisor')
|
10
|
-
|
11
|
-
class SpawnTest < MiniTest::Test
|
12
|
-
def setup
|
13
|
-
EV.rerun
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_that_spawn_returns_a_coprocess
|
17
|
-
result = nil
|
18
|
-
coprocess = spawn { result = 42 }
|
19
|
-
|
20
|
-
assert_kind_of(Coprocess, coprocess)
|
21
|
-
assert_nil(result)
|
22
|
-
suspend
|
23
|
-
assert_equal(42, result)
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_that_spawn_accepts_coprocess_argument
|
27
|
-
result = nil
|
28
|
-
coprocess = Coprocess.new { result = 42 }
|
29
|
-
spawn coprocess
|
30
|
-
|
31
|
-
assert_nil(result)
|
32
|
-
suspend
|
33
|
-
assert_equal(42, result)
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_that_spawned_coprocess_saves_result
|
37
|
-
coprocess = spawn { 42 }
|
38
|
-
|
39
|
-
assert_kind_of(Coprocess, coprocess)
|
40
|
-
assert_nil(coprocess.result)
|
41
|
-
suspend
|
42
|
-
assert_equal(42, coprocess.result)
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_that_spawned_coprocess_can_be_interrupted
|
46
|
-
result = nil
|
47
|
-
coprocess = spawn { sleep(1); 42 }
|
48
|
-
EV.next_tick { coprocess.interrupt }
|
49
|
-
suspend
|
50
|
-
assert_nil(coprocess.result)
|
51
|
-
end
|
5
|
+
class SpawnTest < MiniTest::Test
|
6
|
+
def setup
|
7
|
+
EV.rerun
|
52
8
|
end
|
53
9
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
10
|
+
def test_that_spawn_returns_a_coprocess
|
11
|
+
result = nil
|
12
|
+
coprocess = spawn { result = 42 }
|
58
13
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
suspend
|
66
|
-
assert_equal(42, result)
|
67
|
-
end
|
14
|
+
assert_kind_of(Polyphony::Coprocess, coprocess)
|
15
|
+
assert_nil(result)
|
16
|
+
suspend
|
17
|
+
assert_equal(42, result)
|
18
|
+
end
|
68
19
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
result = 42
|
74
|
-
end
|
75
|
-
EV.next_tick { coprocess.interrupt }
|
76
|
-
suspend
|
77
|
-
assert_nil(result)
|
78
|
-
end
|
20
|
+
def test_that_spawn_accepts_coprocess_argument
|
21
|
+
result = nil
|
22
|
+
coprocess = Polyphony::Coprocess.new { result = 42 }
|
23
|
+
spawn coprocess
|
79
24
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
result = 42
|
85
|
-
rescue Exceptions::Cancel => e
|
86
|
-
result = e
|
87
|
-
end
|
88
|
-
EV.next_tick { coprocess.cancel! }
|
25
|
+
assert_nil(result)
|
26
|
+
suspend
|
27
|
+
assert_equal(42, result)
|
28
|
+
end
|
89
29
|
|
90
|
-
|
30
|
+
def test_that_spawned_coprocess_saves_result
|
31
|
+
coprocess = spawn { 42 }
|
91
32
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
33
|
+
assert_kind_of(Polyphony::Coprocess, coprocess)
|
34
|
+
assert_nil(coprocess.result)
|
35
|
+
suspend
|
36
|
+
assert_equal(42, coprocess.result)
|
37
|
+
end
|
96
38
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
coprocess2.await
|
106
|
-
result && result += 1
|
107
|
-
end
|
108
|
-
EV.next_tick { coprocess.interrupt }
|
109
|
-
suspend
|
110
|
-
assert_nil(result)
|
111
|
-
assert_nil(coprocess.running?)
|
112
|
-
assert_nil(coprocess2.running?)
|
113
|
-
end
|
39
|
+
def test_that_spawned_coprocess_can_be_interrupted
|
40
|
+
result = nil
|
41
|
+
coprocess = spawn { sleep(1); 42 }
|
42
|
+
EV.next_tick { coprocess.interrupt }
|
43
|
+
suspend
|
44
|
+
assert_nil(coprocess.result)
|
45
|
+
end
|
46
|
+
end
|
114
47
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
coprocess = spawn do
|
119
|
-
coprocess2 = spawn do
|
120
|
-
EV.next_tick { coprocess.interrupt }
|
121
|
-
sleep(0.001)
|
122
|
-
result = 42
|
123
|
-
end
|
124
|
-
coprocess2.await
|
125
|
-
result && result += 1
|
126
|
-
end
|
127
|
-
|
128
|
-
suspend
|
129
|
-
|
130
|
-
assert_nil(result)
|
131
|
-
assert_nil(coprocess.running?)
|
132
|
-
assert_nil(coprocess2.running?)
|
133
|
-
end
|
48
|
+
class CancelScopeTest < Minitest::Test
|
49
|
+
def setup
|
50
|
+
EV.rerun
|
134
51
|
end
|
135
52
|
|
136
|
-
|
137
|
-
|
138
|
-
|
53
|
+
def sleep_with_cancel(ctx, mode = nil)
|
54
|
+
Polyphony::CancelScope.new(mode: mode).call do |c|
|
55
|
+
ctx[:cancel_scope] = c
|
56
|
+
ctx[:result] = sleep(0.01)
|
139
57
|
end
|
58
|
+
end
|
140
59
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
60
|
+
def test_that_cancel_scope_cancels_coprocess
|
61
|
+
ctx = {}
|
62
|
+
spawn do
|
63
|
+
EV::Timer.new(0.005, 0).start { ctx[:cancel_scope]&.cancel! }
|
64
|
+
sleep_with_cancel(ctx, :cancel)
|
65
|
+
rescue Exception => e
|
66
|
+
ctx[:result] = e
|
67
|
+
end
|
68
|
+
assert_nil(ctx[:result])
|
69
|
+
# async operation will only begin on next iteration of event loop
|
70
|
+
assert_nil(ctx[:cancel_scope])
|
71
|
+
|
72
|
+
suspend
|
73
|
+
assert_kind_of(Polyphony::CancelScope, ctx[:cancel_scope])
|
74
|
+
assert_kind_of(Polyphony::Cancel, ctx[:result])
|
75
|
+
end
|
147
76
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
77
|
+
# def test_that_cancel_scope_cancels_async_op_with_stop
|
78
|
+
# ctx = {}
|
79
|
+
# spawn do
|
80
|
+
# EV::Timer.new(0, 0).start { ctx[:cancel_scope].cancel! }
|
81
|
+
# sleep_with_cancel(ctx, :stop)
|
82
|
+
# end
|
83
|
+
|
84
|
+
# suspend
|
85
|
+
# assert(ctx[:cancel_scope])
|
86
|
+
# assert_nil(ctx[:result])
|
87
|
+
# end
|
88
|
+
|
89
|
+
def test_that_cancel_after_raises_cancelled_exception
|
90
|
+
result = nil
|
91
|
+
spawn do
|
92
|
+
cancel_after(0.01) do
|
93
|
+
sleep(1000)
|
155
94
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
suspend
|
161
|
-
assert_kind_of(CancelScope, ctx[:cancel_scope])
|
162
|
-
assert_kind_of(Exceptions::Cancel, ctx[:result])
|
95
|
+
result = 42
|
96
|
+
rescue Polyphony::Cancel
|
97
|
+
result = :cancelled
|
163
98
|
end
|
99
|
+
suspend
|
100
|
+
assert_equal(:cancelled, result)
|
101
|
+
end
|
164
102
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
# suspend
|
173
|
-
# assert(ctx[:cancel_scope])
|
174
|
-
# assert_nil(ctx[:result])
|
175
|
-
# end
|
176
|
-
|
177
|
-
def test_that_cancel_after_raises_cancelled_exception
|
178
|
-
result = nil
|
179
|
-
spawn do
|
180
|
-
cancel_after(0.01) do
|
103
|
+
def test_that_cancel_scopes_can_be_nested
|
104
|
+
inner_result = nil
|
105
|
+
outer_result = nil
|
106
|
+
spawn do
|
107
|
+
move_on_after(0.01) do
|
108
|
+
move_on_after(0.02) do
|
181
109
|
sleep(1000)
|
182
110
|
end
|
183
|
-
|
184
|
-
rescue Exceptions::Cancel
|
185
|
-
result = :cancelled
|
111
|
+
inner_result = 42
|
186
112
|
end
|
187
|
-
|
188
|
-
assert_equal(:cancelled, result)
|
113
|
+
outer_result = 42
|
189
114
|
end
|
115
|
+
suspend
|
116
|
+
assert_nil(inner_result)
|
117
|
+
assert_equal(42, outer_result)
|
190
118
|
|
191
|
-
|
192
|
-
inner_result = nil
|
193
|
-
outer_result = nil
|
194
|
-
spawn do
|
195
|
-
move_on_after(0.01) do
|
196
|
-
move_on_after(0.02) do
|
197
|
-
sleep(1000)
|
198
|
-
end
|
199
|
-
inner_result = 42
|
200
|
-
end
|
201
|
-
outer_result = 42
|
202
|
-
end
|
203
|
-
suspend
|
204
|
-
assert_nil(inner_result)
|
205
|
-
assert_equal(42, outer_result)
|
206
|
-
|
207
|
-
EV.rerun
|
119
|
+
EV.rerun
|
208
120
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
215
|
-
inner_result = 42
|
121
|
+
outer_result = nil
|
122
|
+
spawn do
|
123
|
+
move_on_after(0.02) do
|
124
|
+
move_on_after(0.01) do
|
125
|
+
sleep(1000)
|
216
126
|
end
|
217
|
-
|
127
|
+
inner_result = 42
|
218
128
|
end
|
219
|
-
|
220
|
-
assert_equal(42, inner_result)
|
221
|
-
assert_equal(42, outer_result)
|
129
|
+
outer_result = 42
|
222
130
|
end
|
131
|
+
suspend
|
132
|
+
assert_equal(42, inner_result)
|
133
|
+
assert_equal(42, outer_result)
|
223
134
|
end
|
135
|
+
end
|
224
136
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
137
|
+
class SupervisorTest < MiniTest::Test
|
138
|
+
def setup
|
139
|
+
EV.rerun
|
140
|
+
end
|
229
141
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
end
|
142
|
+
def sleep_and_set(ctx, idx)
|
143
|
+
proc do
|
144
|
+
sleep(0.001 * idx)
|
145
|
+
ctx[idx] = true
|
235
146
|
end
|
147
|
+
end
|
236
148
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
def test_that_supervisor_waits_for_all_nested_coprocesss_to_complete
|
244
|
-
ctx = {}
|
245
|
-
spawn do
|
246
|
-
parallel_sleep(ctx)
|
247
|
-
end
|
248
|
-
suspend
|
249
|
-
assert(ctx[1])
|
250
|
-
assert(ctx[2])
|
251
|
-
assert(ctx[3])
|
149
|
+
def parallel_sleep(ctx)
|
150
|
+
supervise do |s|
|
151
|
+
(1..3).each { |idx| s.spawn sleep_and_set(ctx, idx) }
|
252
152
|
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_that_supervisor_waits_for_all_nested_coprocesses_to_complete
|
156
|
+
ctx = {}
|
157
|
+
spawn do
|
158
|
+
parallel_sleep(ctx)
|
159
|
+
end
|
160
|
+
suspend
|
161
|
+
assert(ctx[1])
|
162
|
+
assert(ctx[2])
|
163
|
+
assert(ctx[3])
|
164
|
+
end
|
253
165
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
166
|
+
def test_that_supervisor_can_add_coprocesses_after_having_started
|
167
|
+
result = []
|
168
|
+
spawn {
|
169
|
+
supervisor = Polyphony::Supervisor.new
|
170
|
+
3.times do |i|
|
171
|
+
spawn do
|
172
|
+
sleep(0.001)
|
173
|
+
supervisor.spawn do
|
260
174
|
sleep(0.001)
|
261
|
-
|
262
|
-
sleep(0.001)
|
263
|
-
result << i
|
264
|
-
end
|
175
|
+
result << i
|
265
176
|
end
|
266
177
|
end
|
267
|
-
supervisor.await
|
268
178
|
end
|
179
|
+
supervisor.await
|
180
|
+
}.await
|
269
181
|
|
270
|
-
|
271
|
-
assert_equal([0, 1, 2], result)
|
272
|
-
end
|
182
|
+
assert_equal([0, 1, 2], result)
|
273
183
|
end
|
274
184
|
end
|