polyphony 0.22 → 0.23
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 +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
|