polyphony 0.22 → 0.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile.lock +9 -1
- data/TODO.md +13 -38
- data/docs/summary.md +19 -5
- data/docs/technical-overview/faq.md +12 -0
- data/examples/core/01-spinning-up-coprocesses.rb +2 -6
- data/examples/core/02-awaiting-coprocesses.rb +3 -1
- data/examples/core/03-interrupting.rb +3 -1
- data/examples/core/04-no-auto-run.rb +1 -3
- data/examples/core/cancel.rb +1 -1
- data/examples/core/channel_echo.rb +3 -1
- data/examples/core/defer.rb +3 -1
- data/examples/core/enumerator.rb +3 -1
- data/examples/core/error_bubbling.rb +35 -0
- data/examples/core/fork.rb +1 -1
- data/examples/core/genserver.rb +1 -1
- data/examples/core/lock.rb +3 -1
- data/examples/core/move_on.rb +1 -1
- data/examples/core/move_on_twice.rb +1 -1
- data/examples/core/move_on_with_ensure.rb +1 -1
- data/examples/core/move_on_with_value.rb +1 -1
- data/examples/core/multiple_spin.rb +3 -1
- data/examples/core/nested_cancel.rb +1 -1
- data/examples/core/nested_multiple_spin.rb +3 -1
- data/examples/core/nested_spin.rb +3 -1
- data/examples/core/pulse.rb +1 -1
- data/examples/core/resource.rb +1 -1
- data/examples/core/resource_cancel.rb +2 -2
- data/examples/core/resource_delegate.rb +1 -1
- data/examples/core/sleep.rb +1 -1
- data/examples/core/sleep_spin.rb +3 -1
- data/examples/core/snooze.rb +1 -1
- data/examples/core/spin_error.rb +2 -1
- data/examples/core/spin_error_backtrace.rb +1 -1
- data/examples/core/spin_uncaught_error.rb +3 -1
- data/examples/core/supervisor.rb +1 -1
- data/examples/core/supervisor_with_cancel_scope.rb +1 -1
- data/examples/core/supervisor_with_error.rb +3 -1
- data/examples/core/supervisor_with_manual_move_on.rb +1 -1
- data/examples/core/suspend.rb +1 -1
- data/examples/core/thread.rb +3 -3
- data/examples/core/thread_cancel.rb +6 -3
- data/examples/core/thread_pool.rb +8 -52
- data/examples/core/thread_pool_perf.rb +63 -0
- data/examples/core/throttle.rb +3 -1
- data/examples/core/timeout.rb +1 -1
- data/examples/core/wait_for_signal.rb +4 -2
- data/examples/fs/read.rb +1 -1
- data/examples/http/http2_raw.rb +1 -1
- data/examples/http/http_get.rb +1 -1
- data/examples/http/http_server.rb +2 -1
- data/examples/http/http_server_graceful.rb +3 -1
- data/examples/http/http_ws_server.rb +0 -2
- data/examples/http/https_wss_server.rb +0 -2
- data/examples/http/websocket_secure_server.rb +0 -2
- data/examples/http/websocket_server.rb +0 -2
- data/examples/interfaces/redis_channels.rb +3 -1
- data/examples/interfaces/redis_pubsub.rb +3 -1
- data/examples/interfaces/redis_pubsub_perf.rb +3 -1
- data/examples/io/backticks.rb +1 -1
- data/examples/io/cat.rb +1 -1
- data/examples/io/echo_client.rb +1 -1
- data/examples/io/echo_client_from_stdin.rb +3 -1
- data/examples/io/echo_pipe.rb +1 -1
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_with_timeout.rb +1 -1
- data/examples/io/echo_stdin.rb +1 -1
- data/examples/io/httparty_multi.rb +1 -1
- data/examples/io/io_read.rb +1 -1
- data/examples/io/irb.rb +1 -1
- data/examples/io/net-http.rb +1 -1
- data/examples/io/open.rb +1 -1
- data/examples/io/system.rb +1 -1
- data/examples/io/tcpserver.rb +1 -1
- data/examples/io/tcpsocket.rb +1 -1
- data/examples/performance/multi_snooze.rb +1 -1
- data/examples/performance/snooze.rb +18 -10
- data/ext/gyro/async.c +16 -9
- data/ext/gyro/child.c +2 -2
- data/ext/gyro/gyro.c +17 -10
- data/ext/gyro/gyro.h +2 -2
- data/ext/gyro/io.c +2 -2
- data/ext/gyro/signal.c +33 -35
- data/ext/gyro/timer.c +6 -73
- data/lib/polyphony.rb +6 -8
- data/lib/polyphony/core/cancel_scope.rb +32 -21
- data/lib/polyphony/core/coprocess.rb +26 -23
- data/lib/polyphony/core/global_api.rb +86 -0
- data/lib/polyphony/core/resource_pool.rb +1 -1
- data/lib/polyphony/core/supervisor.rb +47 -13
- data/lib/polyphony/core/thread.rb +10 -36
- data/lib/polyphony/core/thread_pool.rb +6 -26
- data/lib/polyphony/extensions/core.rb +30 -100
- data/lib/polyphony/extensions/io.rb +10 -7
- data/lib/polyphony/extensions/openssl.rb +18 -28
- data/lib/polyphony/http/client/agent.rb +15 -11
- data/lib/polyphony/http/client/http2.rb +1 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -0
- data/test/coverage.rb +45 -0
- data/test/helper.rb +15 -5
- data/test/test_async.rb +4 -4
- data/test/test_cancel_scope.rb +109 -0
- data/test/test_coprocess.rb +80 -36
- data/test/{test_core.rb → test_global_api.rb} +67 -13
- data/test/test_gyro.rb +1 -5
- data/test/test_io.rb +2 -2
- data/test/test_resource_pool.rb +19 -0
- data/test/test_signal.rb +10 -5
- data/test/test_supervisor.rb +168 -0
- data/test/test_timer.rb +31 -5
- metadata +23 -4
- data/lib/polyphony/auto_run.rb +0 -19
@@ -25,13 +25,16 @@ class ::IO
|
|
25
25
|
|
26
26
|
alias_method :orig_foreach, :foreach
|
27
27
|
def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
28
|
+
# IO.orig_read(name).each_line(&block)
|
29
|
+
raise NotImplementedError
|
30
|
+
|
31
|
+
# if sep.is_a?(Integer)
|
32
|
+
# sep = $/
|
33
|
+
# limit = sep
|
34
|
+
# end
|
35
|
+
# File.open(name, 'r') do |f|
|
36
|
+
# f.each_line(sep, limit, getline_args, &block)
|
37
|
+
# end
|
35
38
|
end
|
36
39
|
|
37
40
|
alias_method :orig_read, :read
|
@@ -19,29 +19,24 @@ class ::OpenSSL::SSL::SSLSocket
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def sysread(maxlen, buf)
|
22
|
+
read_watcher = nil
|
23
|
+
write_watcher = nil
|
22
24
|
loop do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
read_watcher ||= Gyro::IO.new(io, :r)
|
28
|
-
read_watcher.await
|
29
|
-
elsif result == :wait_writable
|
30
|
-
write_watcher ||= Gyro::IO.new(io, :w)
|
31
|
-
write_watcher.await
|
32
|
-
else
|
33
|
-
return result
|
25
|
+
case (result = read_nonblock(maxlen, buf, exception: false))
|
26
|
+
when :wait_readable then (read_watcher ||= Gyro::IO.new(io, :r)).await
|
27
|
+
when :wait_writable then (write_watcher ||= Gyro::IO.new(io, :w)).await
|
28
|
+
else result
|
34
29
|
end
|
35
30
|
end
|
36
31
|
end
|
37
32
|
|
38
33
|
def flush
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
34
|
+
# osync = @sync
|
35
|
+
# @sync = true
|
36
|
+
# do_write ""
|
37
|
+
# return self
|
38
|
+
# ensure
|
39
|
+
# @sync = osync
|
45
40
|
end
|
46
41
|
|
47
42
|
# def do_write(s)
|
@@ -62,18 +57,13 @@ class ::OpenSSL::SSL::SSLSocket
|
|
62
57
|
# end
|
63
58
|
|
64
59
|
def syswrite(buf)
|
60
|
+
read_watcher = nil
|
61
|
+
write_watcher = nil
|
65
62
|
loop do
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
read_watcher ||= Gyro::IO.new(io, :r)
|
71
|
-
read_watcher.await
|
72
|
-
elsif result == :wait_writable
|
73
|
-
write_watcher ||= Gyro::IO.new(io, :w)
|
74
|
-
write_watcher.await
|
75
|
-
else
|
76
|
-
return result
|
63
|
+
case (result = write_nonblock(buf, exception: false))
|
64
|
+
when :wait_readable then (read_watcher ||= Gyro::IO.new(io, :r)).await
|
65
|
+
when :wait_writable then (write_watcher ||= Gyro::IO.new(io, :w)).await
|
66
|
+
else result
|
77
67
|
end
|
78
68
|
end
|
79
69
|
end
|
@@ -104,23 +104,27 @@ class Agent
|
|
104
104
|
key = uri_key(ctx[:uri])
|
105
105
|
|
106
106
|
@pools[key].acquire do |adapter|
|
107
|
-
|
108
|
-
case response.status_code
|
109
|
-
when 200, 204
|
110
|
-
if block
|
111
|
-
block.(response)
|
112
|
-
else
|
113
|
-
# read body
|
114
|
-
response.body
|
115
|
-
end
|
116
|
-
end
|
117
|
-
response
|
107
|
+
send_request_and_check_response(adapter, ctx, &block)
|
118
108
|
end
|
119
109
|
rescue Exception => e
|
120
110
|
p e
|
121
111
|
puts e.backtrace.join("\n")
|
122
112
|
end
|
123
113
|
|
114
|
+
def send_request_and_check_response(adapter, ctx, &block)
|
115
|
+
response = adapter.request(ctx)
|
116
|
+
case response.status_code
|
117
|
+
when 200, 204
|
118
|
+
if block
|
119
|
+
block.(response)
|
120
|
+
else
|
121
|
+
# read body
|
122
|
+
response.body
|
123
|
+
end
|
124
|
+
end
|
125
|
+
response
|
126
|
+
end
|
127
|
+
|
124
128
|
def uri_key(uri)
|
125
129
|
{ scheme: uri.scheme, host: uri.host, port: uri.port }
|
126
130
|
end
|
data/lib/polyphony/version.rb
CHANGED
data/polyphony.gemspec
CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_development_dependency 'localhost', '1.1.4'
|
32
32
|
s.add_development_dependency 'minitest', '5.11.3'
|
33
33
|
s.add_development_dependency 'minitest-reporters', '1.4.2'
|
34
|
+
s.add_development_dependency 'simplecov', '0.17.1'
|
34
35
|
s.add_development_dependency 'pg', '1.1.3'
|
35
36
|
s.add_development_dependency 'rake-compiler', '1.0.5'
|
36
37
|
s.add_development_dependency 'redis', '4.1.0'
|
data/test/coverage.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'coverage'
|
4
|
+
require 'simplecov'
|
5
|
+
|
6
|
+
class << SimpleCov::LinesClassifier
|
7
|
+
alias_method :orig_whitespace_line?, :whitespace_line?
|
8
|
+
def whitespace_line?(line)
|
9
|
+
line.strip =~ /^(begin|end|ensure|else|\})|(\s*rescue\s.+)$/ || orig_whitespace_line?(line)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Coverage
|
14
|
+
EXCLUDE = %w{coverage eg helper run
|
15
|
+
}.map { |n| File.expand_path("test/#{n}.rb") }
|
16
|
+
|
17
|
+
LIB_FILES = Dir["#{File.join(FileUtils.pwd, 'lib')}/polyphony/**/*.rb"]
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def relevant_lines_for_filename(filename)
|
21
|
+
@classifier ||= SimpleCov::LinesClassifier.new
|
22
|
+
@classifier.classify(IO.read(filename).lines)
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
@result = {}
|
27
|
+
trace = TracePoint.new(:line) do |tp|
|
28
|
+
next if tp.path =~ /\(/
|
29
|
+
|
30
|
+
absolute = File.expand_path(tp.path)
|
31
|
+
next unless LIB_FILES.include?(absolute)# =~ /^#{LIB_DIR}/
|
32
|
+
|
33
|
+
@result[absolute] ||= relevant_lines_for_filename(absolute)
|
34
|
+
@result[absolute][tp.lineno - 1] = 1
|
35
|
+
end
|
36
|
+
trace.enable
|
37
|
+
end
|
38
|
+
|
39
|
+
def result
|
40
|
+
@result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
SimpleCov.start
|
data/test/helper.rb
CHANGED
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
|
5
|
+
require 'fileutils'
|
6
|
+
require_relative './eg'
|
7
|
+
|
8
|
+
require_relative './coverage' if ENV['COVERAGE']
|
9
|
+
|
5
10
|
require 'minitest/autorun'
|
6
11
|
require 'minitest/reporters'
|
7
12
|
|
8
13
|
require 'polyphony'
|
9
|
-
require 'fileutils'
|
10
|
-
|
11
|
-
require_relative './eg'
|
12
14
|
|
13
15
|
::Exception.__disable_sanitized_backtrace__ = true
|
14
16
|
|
@@ -18,8 +20,16 @@ Minitest::Reporters.use! [
|
|
18
20
|
|
19
21
|
class MiniTest::Test
|
20
22
|
def teardown
|
21
|
-
# wait for
|
22
|
-
|
23
|
+
# wait for any remaining scheduled work
|
24
|
+
Gyro.run
|
23
25
|
Polyphony.reset!
|
24
26
|
end
|
25
27
|
end
|
28
|
+
|
29
|
+
module Kernel
|
30
|
+
def capture_exception
|
31
|
+
yield
|
32
|
+
rescue Exception => e
|
33
|
+
e
|
34
|
+
end
|
35
|
+
end
|
data/test/test_async.rb
CHANGED
@@ -12,7 +12,7 @@ class AsyncTest < MiniTest::Test
|
|
12
12
|
}
|
13
13
|
snooze
|
14
14
|
Thread.new do
|
15
|
-
|
15
|
+
orig_sleep 0.001
|
16
16
|
a.signal!
|
17
17
|
end
|
18
18
|
suspend
|
@@ -26,15 +26,15 @@ class AsyncTest < MiniTest::Test
|
|
26
26
|
loop {
|
27
27
|
a.await
|
28
28
|
count += 1
|
29
|
-
|
29
|
+
defer { coproc.stop }
|
30
30
|
}
|
31
31
|
}
|
32
32
|
snooze
|
33
33
|
Thread.new do
|
34
|
-
|
34
|
+
orig_sleep 0.001
|
35
35
|
3.times { a.signal! }
|
36
36
|
end
|
37
|
-
|
37
|
+
coproc.await
|
38
38
|
assert_equal(1, count)
|
39
39
|
end
|
40
40
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class CancelScopeTest < MiniTest::Test
|
6
|
+
def test_that_cancel_scope_can_cancel_provided_block
|
7
|
+
buffer = []
|
8
|
+
Polyphony::CancelScope.new { |scope|
|
9
|
+
defer { scope.cancel! }
|
10
|
+
buffer << 1
|
11
|
+
snooze
|
12
|
+
buffer << 2
|
13
|
+
}
|
14
|
+
assert_equal [1], buffer
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_that_cancel_scope_can_cancel_multiple_coprocesses
|
18
|
+
buffer = []
|
19
|
+
scope = Polyphony::CancelScope.new
|
20
|
+
coprocs = (1..3).map { |i|
|
21
|
+
spin {
|
22
|
+
scope.call do
|
23
|
+
buffer << i
|
24
|
+
snooze
|
25
|
+
buffer << i * 10
|
26
|
+
end
|
27
|
+
}
|
28
|
+
}
|
29
|
+
snooze
|
30
|
+
scope.cancel!
|
31
|
+
assert_equal [1, 2, 3], buffer
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_that_cancel_scope_takes_timeout_option
|
35
|
+
buffer = []
|
36
|
+
Polyphony::CancelScope.new(timeout: 0.01) { |scope|
|
37
|
+
buffer << 1
|
38
|
+
sleep 0.02
|
39
|
+
buffer << 2
|
40
|
+
}
|
41
|
+
assert_equal [1], buffer
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_that_cancel_scope_cancels_timeout_waiter_if_block_provided
|
45
|
+
buffer = []
|
46
|
+
t0 = Time.now
|
47
|
+
scope = Polyphony::CancelScope.new(timeout: 1) { |scope|
|
48
|
+
buffer << 1
|
49
|
+
}
|
50
|
+
assert_equal [1], buffer
|
51
|
+
assert Time.now - t0 < 1
|
52
|
+
assert_nil scope.instance_variable_get(:@timeout_waiter)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_that_cancel_scope_can_cancel_multiple_coprocs_with_timeout
|
56
|
+
buffer = []
|
57
|
+
t0 = Time.now
|
58
|
+
scope = Polyphony::CancelScope.new(timeout: 0.02)
|
59
|
+
coprocs = (1..3).map { |i|
|
60
|
+
spin {
|
61
|
+
scope.call do
|
62
|
+
buffer << i
|
63
|
+
sleep i
|
64
|
+
buffer << i * 10
|
65
|
+
end
|
66
|
+
}
|
67
|
+
}
|
68
|
+
Polyphony::Coprocess.await(*coprocs)
|
69
|
+
assert Time.now - t0 < 0.05
|
70
|
+
assert_equal [1, 2, 3], buffer
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_reset_timeout
|
74
|
+
buffer = []
|
75
|
+
scope = Polyphony::CancelScope.new(timeout: 0.01)
|
76
|
+
t0 = Time.now
|
77
|
+
scope.call {
|
78
|
+
sleep 0.005
|
79
|
+
scope.reset_timeout
|
80
|
+
sleep 0.010
|
81
|
+
}
|
82
|
+
|
83
|
+
assert !scope.cancelled?
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_on_cancel
|
87
|
+
buffer = []
|
88
|
+
Polyphony::CancelScope.new { |scope|
|
89
|
+
defer { scope.cancel! }
|
90
|
+
scope.on_cancel { buffer << :cancelled }
|
91
|
+
buffer << 1
|
92
|
+
snooze
|
93
|
+
buffer << 2
|
94
|
+
}
|
95
|
+
assert_equal [1, :cancelled], buffer
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_cancelled?
|
99
|
+
scope = Polyphony::CancelScope.new
|
100
|
+
spin {
|
101
|
+
scope.call { sleep 1 }
|
102
|
+
}
|
103
|
+
|
104
|
+
snooze
|
105
|
+
assert !scope.cancelled?
|
106
|
+
scope.cancel!
|
107
|
+
assert scope.cancelled?
|
108
|
+
end
|
109
|
+
end
|
data/test/test_coprocess.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
|
-
STDOUT.sync = true
|
6
|
-
|
7
5
|
class CoprocessTest < MiniTest::Test
|
8
6
|
def test_that_root_fiber_has_associated_coprocess
|
9
7
|
assert_equal(Fiber.current, Polyphony::Coprocess.current.fiber)
|
@@ -230,6 +228,16 @@ class CoprocessTest < MiniTest::Test
|
|
230
228
|
assert_equal('foo', raised_error.message)
|
231
229
|
end
|
232
230
|
|
231
|
+
def test_that_coprocess_can_be_cancelled_before_first_scheduling
|
232
|
+
buffer = []
|
233
|
+
coproc = spin { buffer << 1 }
|
234
|
+
coproc.stop
|
235
|
+
|
236
|
+
snooze
|
237
|
+
assert_nil coproc.alive?
|
238
|
+
assert_equal [], buffer
|
239
|
+
end
|
240
|
+
|
233
241
|
def test_exception_propagation_for_orphan_fiber
|
234
242
|
raised_error = nil
|
235
243
|
spin do
|
@@ -255,6 +263,15 @@ class CoprocessTest < MiniTest::Test
|
|
255
263
|
assert_equal %i{foo bar baz}, result
|
256
264
|
end
|
257
265
|
|
266
|
+
def test_join_multiple_coprocesses
|
267
|
+
cp1 = spin { sleep 0.01; :foo }
|
268
|
+
cp2 = spin { sleep 0.01; :bar }
|
269
|
+
cp3 = spin { sleep 0.01; :baz }
|
270
|
+
|
271
|
+
result = Polyphony::Coprocess.join(cp1, cp2, cp3)
|
272
|
+
assert_equal %i{foo bar baz}, result
|
273
|
+
end
|
274
|
+
|
258
275
|
def test_caller
|
259
276
|
location = /^#{__FILE__}:#{__LINE__ + 1}/
|
260
277
|
cp = spin do
|
@@ -263,7 +280,7 @@ class CoprocessTest < MiniTest::Test
|
|
263
280
|
snooze
|
264
281
|
|
265
282
|
caller = cp.caller
|
266
|
-
|
283
|
+
assert_match location, caller[0]
|
267
284
|
end
|
268
285
|
|
269
286
|
def test_location
|
@@ -275,39 +292,6 @@ class CoprocessTest < MiniTest::Test
|
|
275
292
|
|
276
293
|
assert cp.location =~ location
|
277
294
|
end
|
278
|
-
end
|
279
|
-
|
280
|
-
class MailboxTest < MiniTest::Test
|
281
|
-
def test_that_coprocess_can_receive_messages
|
282
|
-
msgs = []
|
283
|
-
coproc = spin { loop { msgs << receive } }
|
284
|
-
|
285
|
-
snooze # allow coproc to start
|
286
|
-
|
287
|
-
3.times do |i|
|
288
|
-
coproc << i
|
289
|
-
snooze
|
290
|
-
end
|
291
|
-
|
292
|
-
assert_equal([0, 1, 2], msgs)
|
293
|
-
ensure
|
294
|
-
coproc&.stop
|
295
|
-
end
|
296
|
-
|
297
|
-
def test_that_multiple_messages_sent_at_once_arrive_in_order
|
298
|
-
msgs = []
|
299
|
-
coproc = spin { loop { msgs << receive } }
|
300
|
-
|
301
|
-
snooze # allow coproc to start
|
302
|
-
|
303
|
-
3.times { |i| coproc << i }
|
304
|
-
|
305
|
-
snooze
|
306
|
-
|
307
|
-
assert_equal([0, 1, 2], msgs)
|
308
|
-
ensure
|
309
|
-
coproc&.stop
|
310
|
-
end
|
311
295
|
|
312
296
|
def test_when_done
|
313
297
|
flag = nil
|
@@ -394,3 +378,63 @@ class MailboxTest < MiniTest::Test
|
|
394
378
|
assert_equal :ok, value
|
395
379
|
end
|
396
380
|
end
|
381
|
+
|
382
|
+
class MailboxTest < MiniTest::Test
|
383
|
+
def test_that_coprocess_can_receive_messages
|
384
|
+
msgs = []
|
385
|
+
coproc = spin { loop { msgs << receive } }
|
386
|
+
|
387
|
+
snooze # allow coproc to start
|
388
|
+
|
389
|
+
3.times do |i|
|
390
|
+
coproc << i
|
391
|
+
snooze
|
392
|
+
end
|
393
|
+
|
394
|
+
assert_equal([0, 1, 2], msgs)
|
395
|
+
ensure
|
396
|
+
coproc&.stop
|
397
|
+
end
|
398
|
+
|
399
|
+
def test_that_multiple_messages_sent_at_once_arrive_in_order
|
400
|
+
msgs = []
|
401
|
+
coproc = spin { loop { msgs << receive } }
|
402
|
+
|
403
|
+
snooze # allow coproc to start
|
404
|
+
|
405
|
+
3.times { |i| coproc << i }
|
406
|
+
|
407
|
+
snooze
|
408
|
+
|
409
|
+
assert_equal([0, 1, 2], msgs)
|
410
|
+
ensure
|
411
|
+
coproc&.stop
|
412
|
+
end
|
413
|
+
|
414
|
+
def test_that_sent_message_are_queued_before_calling_receive
|
415
|
+
buffer = []
|
416
|
+
receiver = spin { suspend; 3.times { buffer << receive } }
|
417
|
+
sender = spin { 3.times { |i| receiver << (i * 10) } }
|
418
|
+
|
419
|
+
sender.await
|
420
|
+
receiver.schedule
|
421
|
+
receiver.await
|
422
|
+
|
423
|
+
assert_equal [0, 10, 20], buffer
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_map_and_count
|
427
|
+
assert_equal 1, Polyphony::Coprocess.count
|
428
|
+
map = { Fiber.current => Polyphony::Coprocess.current }
|
429
|
+
assert_equal map, Polyphony::Coprocess.map
|
430
|
+
|
431
|
+
cp = spin { sleep 1 }
|
432
|
+
snooze
|
433
|
+
assert_equal 2, Polyphony::Coprocess.count
|
434
|
+
assert_equal cp, Polyphony::Coprocess.map[cp.fiber]
|
435
|
+
|
436
|
+
cp.stop
|
437
|
+
snooze
|
438
|
+
assert_equal 1, Polyphony::Coprocess.count
|
439
|
+
end
|
440
|
+
end
|