polyphony 0.38 → 0.43
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/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +25 -2
- data/Gemfile.lock +15 -12
- data/README.md +2 -1
- data/Rakefile +3 -3
- data/TODO.md +27 -97
- data/docs/_config.yml +56 -7
- data/docs/_sass/custom/custom.scss +0 -30
- data/docs/_sass/overrides.scss +0 -46
- data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
- data/docs/_user-guide/index.md +9 -0
- data/docs/{user-guide → _user-guide}/web-server.md +0 -0
- data/docs/api-reference/fiber.md +2 -2
- data/docs/api-reference/index.md +9 -0
- data/docs/api-reference/polyphony-process.md +1 -1
- data/docs/api-reference/thread.md +1 -1
- data/docs/faq.md +21 -11
- data/docs/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +2 -6
- data/docs/getting-started/overview.md +486 -0
- data/docs/getting-started/tutorial.md +27 -19
- data/docs/index.md +1 -1
- data/docs/main-concepts/concurrency.md +0 -5
- data/docs/main-concepts/design-principles.md +69 -21
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/index.md +9 -0
- data/examples/core/01-spinning-up-fibers.rb +1 -0
- data/examples/core/03-interrupting.rb +4 -1
- data/examples/core/04-handling-signals.rb +19 -0
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/io/tunnel.rb +48 -0
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
- data/examples/performance/xx-array.rb +11 -0
- data/examples/performance/xx-fiber-switch.rb +9 -0
- data/examples/performance/xx-snooze.rb +15 -0
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/{gyro → polyphony}/fiber.c +17 -23
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +718 -0
- data/ext/polyphony/libev_queue.c +216 -0
- data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -40
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +19 -39
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +21 -18
- data/ext/polyphony/thread.c +206 -0
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +19 -14
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +5 -5
- data/lib/polyphony/adapters/trace.rb +28 -28
- data/lib/polyphony/core/channel.rb +3 -3
- data/lib/polyphony/core/exceptions.rb +1 -1
- data/lib/polyphony/core/global_api.rb +13 -11
- data/lib/polyphony/core/resource_pool.rb +3 -3
- data/lib/polyphony/core/sync.rb +2 -2
- data/lib/polyphony/core/thread_pool.rb +6 -6
- data/lib/polyphony/core/throttler.rb +13 -6
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +22 -14
- data/lib/polyphony/extensions/fiber.rb +4 -4
- data/lib/polyphony/extensions/io.rb +59 -25
- data/lib/polyphony/extensions/openssl.rb +36 -16
- data/lib/polyphony/extensions/socket.rb +27 -9
- data/lib/polyphony/extensions/thread.rb +16 -9
- data/lib/polyphony/net.rb +9 -9
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +4 -4
- data/test/helper.rb +14 -1
- data/test/test_agent.rb +124 -0
- data/test/{test_async.rb → test_event.rb} +15 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +4 -4
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +34 -0
- data/test/test_thread.rb +37 -16
- data/test/test_trace.rb +6 -5
- metadata +40 -43
- data/docs/_includes/nav.html +0 -51
- data/docs/_includes/prevnext.html +0 -17
- data/docs/_layouts/default.html +0 -106
- data/docs/api-reference.md +0 -11
- data/docs/api-reference/gyro-async.md +0 -57
- data/docs/api-reference/gyro-child.md +0 -29
- data/docs/api-reference/gyro-queue.md +0 -44
- data/docs/api-reference/gyro-timer.md +0 -51
- data/docs/api-reference/gyro.md +0 -25
- data/docs/getting-started.md +0 -10
- data/docs/main-concepts.md +0 -10
- data/docs/user-guide.md +0 -10
- data/examples/core/forever_sleep.rb +0 -19
- data/ext/gyro/async.c +0 -162
- data/ext/gyro/child.c +0 -141
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -489
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -228
- data/ext/gyro/signal.c +0 -133
- data/ext/gyro/thread.c +0 -308
- data/ext/gyro/timer.c +0 -149
- data/test/test_timer.rb +0 -56
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'openssl'
|
4
|
-
|
5
4
|
require_relative './socket'
|
6
5
|
|
7
6
|
# Open ssl socket helper methods (to make it compatible with Socket API)
|
@@ -18,14 +17,36 @@ class ::OpenSSL::SSL::SSLSocket
|
|
18
17
|
io.reuse_addr
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
alias_method :orig_accept, :accept
|
21
|
+
def accept
|
22
|
+
loop do
|
23
|
+
result = accept_nonblock(exception: false)
|
24
|
+
case result
|
25
|
+
when :wait_readable then Thread.current.agent.wait_io(io, false)
|
26
|
+
when :wait_writable then Thread.current.agent.wait_io(io, true)
|
27
|
+
else
|
28
|
+
return result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :orig_sysread, :sysread
|
34
|
+
def sysread(maxlen, buf = +'')
|
24
35
|
loop do
|
25
36
|
case (result = read_nonblock(maxlen, buf, exception: false))
|
26
|
-
when :wait_readable then (
|
27
|
-
|
28
|
-
|
37
|
+
when :wait_readable then Thread.current.agent.wait_io(io, false)
|
38
|
+
else return result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :orig_syswrite, :syswrite
|
44
|
+
def syswrite(buf)
|
45
|
+
loop do
|
46
|
+
case (result = write_nonblock(buf, exception: false))
|
47
|
+
when :wait_writable then Thread.current.agent.wait_io(io, true)
|
48
|
+
else
|
49
|
+
return result
|
29
50
|
end
|
30
51
|
end
|
31
52
|
end
|
@@ -39,15 +60,14 @@ class ::OpenSSL::SSL::SSLSocket
|
|
39
60
|
# @sync = osync
|
40
61
|
end
|
41
62
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
63
|
+
def readpartial(maxlen, buf = +'')
|
64
|
+
result = sysread(maxlen, buf)
|
65
|
+
result || (raise EOFError)
|
66
|
+
end
|
67
|
+
|
68
|
+
def read_loop
|
69
|
+
while (data = sysread(8192))
|
70
|
+
yield data
|
51
71
|
end
|
52
72
|
end
|
53
73
|
end
|
@@ -7,14 +7,21 @@ require_relative '../core/thread_pool'
|
|
7
7
|
|
8
8
|
# Socket overrides (eventually rewritten in C)
|
9
9
|
class ::Socket
|
10
|
+
def accept
|
11
|
+
Thread.current.agent.accept(self)
|
12
|
+
end
|
13
|
+
|
10
14
|
NO_EXCEPTION = { exception: false }.freeze
|
11
15
|
|
12
16
|
def connect(remotesockaddr)
|
13
17
|
loop do
|
14
18
|
result = connect_nonblock(remotesockaddr, **NO_EXCEPTION)
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
case result
|
20
|
+
when 0 then return
|
21
|
+
when :wait_writable then Thread.current.agent.wait_io(self, true)
|
22
|
+
else
|
23
|
+
raise IOError
|
24
|
+
end
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
@@ -22,9 +29,12 @@ class ::Socket
|
|
22
29
|
outbuf ||= +''
|
23
30
|
loop do
|
24
31
|
result = recv_nonblock(maxlen, flags, outbuf, **NO_EXCEPTION)
|
25
|
-
|
26
|
-
|
27
|
-
|
32
|
+
case result
|
33
|
+
when nil then raise IOError
|
34
|
+
when :wait_readable then Thread.current.agent.wait_io(self, false)
|
35
|
+
else
|
36
|
+
return result
|
37
|
+
end
|
28
38
|
end
|
29
39
|
end
|
30
40
|
|
@@ -32,9 +42,12 @@ class ::Socket
|
|
32
42
|
@read_buffer ||= +''
|
33
43
|
loop do
|
34
44
|
result = recvfrom_nonblock(maxlen, flags, @read_buffer, **NO_EXCEPTION)
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
case result
|
46
|
+
when nil then raise IOError
|
47
|
+
when :wait_readable then Thread.current.agent.wait_io(self, false)
|
48
|
+
else
|
49
|
+
return result
|
50
|
+
end
|
38
51
|
end
|
39
52
|
end
|
40
53
|
|
@@ -117,4 +130,9 @@ class ::TCPServer
|
|
117
130
|
def accept
|
118
131
|
@io ? @io.accept : orig_accept
|
119
132
|
end
|
133
|
+
|
134
|
+
alias_method :orig_close, :close
|
135
|
+
def close
|
136
|
+
@io ? @io.close : orig_close
|
137
|
+
end
|
120
138
|
end
|
@@ -8,26 +8,30 @@ class ::Thread
|
|
8
8
|
|
9
9
|
alias_method :orig_initialize, :initialize
|
10
10
|
def initialize(*args, &block)
|
11
|
-
@join_wait_queue =
|
11
|
+
@join_wait_queue = []
|
12
|
+
@finalization_mutex = Mutex.new
|
12
13
|
@args = args
|
13
14
|
@block = block
|
14
|
-
@finalization_mutex = Mutex.new
|
15
15
|
orig_initialize { execute }
|
16
16
|
end
|
17
17
|
|
18
18
|
def execute
|
19
|
+
# agent must be created in the context of the new thread, therefore it
|
20
|
+
# cannot be created in Thread#initialize
|
21
|
+
@agent = Polyphony::LibevAgent.new
|
19
22
|
setup
|
20
23
|
@ready = true
|
21
24
|
result = @block.(*@args)
|
22
25
|
rescue Polyphony::MoveOn, Polyphony::Terminate => e
|
23
26
|
result = e.value
|
24
|
-
rescue Exception =>
|
25
|
-
result = e
|
27
|
+
rescue Exception => result
|
26
28
|
ensure
|
27
29
|
@ready = true
|
28
30
|
finalize(result)
|
29
31
|
end
|
30
32
|
|
33
|
+
attr_accessor :agent
|
34
|
+
|
31
35
|
def setup
|
32
36
|
@main_fiber = Fiber.current
|
33
37
|
@main_fiber.setup_main_fiber
|
@@ -44,24 +48,25 @@ class ::Thread
|
|
44
48
|
@result = result
|
45
49
|
signal_waiters(result)
|
46
50
|
end
|
47
|
-
|
51
|
+
@agent.finalize
|
48
52
|
end
|
49
53
|
|
50
54
|
def signal_waiters(result)
|
51
|
-
@join_wait_queue.
|
55
|
+
@join_wait_queue.each { |w| w.signal(result) }
|
52
56
|
end
|
53
57
|
|
54
58
|
alias_method :orig_join, :join
|
55
59
|
def join(timeout = nil)
|
56
|
-
|
60
|
+
watcher = Fiber.current.auto_watcher
|
61
|
+
|
57
62
|
@finalization_mutex.synchronize do
|
58
63
|
if @terminated
|
59
64
|
@result.is_a?(Exception) ? (raise @result) : (return @result)
|
60
65
|
else
|
61
|
-
@join_wait_queue
|
66
|
+
@join_wait_queue << watcher
|
62
67
|
end
|
63
68
|
end
|
64
|
-
timeout ? move_on_after(timeout) {
|
69
|
+
timeout ? move_on_after(timeout) { watcher.await } : watcher.await
|
65
70
|
end
|
66
71
|
alias_method :await, :join
|
67
72
|
|
@@ -78,6 +83,8 @@ class ::Thread
|
|
78
83
|
|
79
84
|
alias_method :orig_kill, :kill
|
80
85
|
def kill
|
86
|
+
return if @terminated
|
87
|
+
|
81
88
|
raise Polyphony::Terminate
|
82
89
|
end
|
83
90
|
|
data/lib/polyphony/net.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative './extensions/socket'
|
|
4
4
|
require_relative './extensions/openssl'
|
5
5
|
|
6
6
|
module Polyphony
|
7
|
+
# A more elegant networking API
|
7
8
|
module Net
|
8
9
|
class << self
|
9
10
|
def tcp_connect(host, port, opts = {})
|
@@ -17,11 +18,11 @@ module Polyphony
|
|
17
18
|
socket
|
18
19
|
end
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
def tcp_listen(host = nil, port = nil, opts = {})
|
22
23
|
host ||= '0.0.0.0'
|
23
24
|
raise 'Port number not specified' unless port
|
24
|
-
|
25
|
+
|
25
26
|
socket = socket_from_options(host, port, opts)
|
26
27
|
if opts[:secure_context] || opts[:secure]
|
27
28
|
secure_server(socket, opts[:secure_context], opts)
|
@@ -29,7 +30,7 @@ module Polyphony
|
|
29
30
|
socket
|
30
31
|
end
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
def socket_from_options(host, port, opts)
|
34
35
|
::Socket.new(:INET, :STREAM).tap do |s|
|
35
36
|
s.reuse_addr if opts[:reuse_addr]
|
@@ -39,19 +40,19 @@ module Polyphony
|
|
39
40
|
s.listen(0)
|
40
41
|
end
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
def secure_socket(socket, context, opts)
|
44
45
|
context ||= OpenSSL::SSL::SSLContext.new
|
45
46
|
setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
|
46
47
|
socket = secure_socket_wrapper(socket, context)
|
47
|
-
|
48
|
+
|
48
49
|
socket.tap do |s|
|
49
50
|
s.hostname = opts[:host] if opts[:host]
|
50
51
|
s.connect
|
51
52
|
s.post_connection_check(opts[:host]) if opts[:host]
|
52
53
|
end
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
def secure_socket_wrapper(socket, context)
|
56
57
|
if context
|
57
58
|
OpenSSL::SSL::SSLSocket.new(socket, context)
|
@@ -59,12 +60,12 @@ module Polyphony
|
|
59
60
|
OpenSSL::SSL::SSLSocket.new(socket)
|
60
61
|
end
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def secure_server(socket, context, opts)
|
64
65
|
setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
|
65
66
|
OpenSSL::SSL::SSLServer.new(socket, context)
|
66
67
|
end
|
67
|
-
|
68
|
+
|
68
69
|
def setup_alpn(context, protocols)
|
69
70
|
context.alpn_protocols = protocols
|
70
71
|
context.alpn_select_cb = lambda do |peer_protocols|
|
@@ -74,4 +75,3 @@ module Polyphony
|
|
74
75
|
end
|
75
76
|
end
|
76
77
|
end
|
77
|
-
|
data/lib/polyphony/version.rb
CHANGED
data/polyphony.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
}
|
18
18
|
s.rdoc_options = ["--title", "polyphony", "--main", "README.md"]
|
19
19
|
s.extra_rdoc_files = ["README.md"]
|
20
|
-
s.extensions = ["ext/
|
20
|
+
s.extensions = ["ext/polyphony/extconf.rb"]
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.required_ruby_version = '>= 2.6'
|
23
23
|
|
@@ -26,8 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_development_dependency 'minitest', '5.13.0'
|
27
27
|
s.add_development_dependency 'minitest-reporters', '1.4.2'
|
28
28
|
s.add_development_dependency 'simplecov', '0.17.1'
|
29
|
-
s.add_development_dependency 'rubocop', '0.
|
30
|
-
s.add_development_dependency 'pg', '1.1.
|
29
|
+
s.add_development_dependency 'rubocop', '0.85.1'
|
30
|
+
s.add_development_dependency 'pg', '1.1.4'
|
31
31
|
s.add_development_dependency 'rake-compiler', '1.0.5'
|
32
32
|
s.add_development_dependency 'redis', '4.1.0'
|
33
33
|
s.add_development_dependency 'hiredis', '0.6.3'
|
@@ -36,5 +36,5 @@ Gem::Specification.new do |s|
|
|
36
36
|
s.add_development_dependency 'jekyll', '~>3.8.6'
|
37
37
|
s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
|
38
38
|
s.add_development_dependency 'jekyll-seo-tag', '~>2.6.1'
|
39
|
-
s.add_development_dependency 'just-the-docs', '~>0.
|
39
|
+
s.add_development_dependency 'just-the-docs', '~>0.3.0'
|
40
40
|
end
|
data/test/helper.rb
CHANGED
@@ -18,19 +18,32 @@ Minitest::Reporters.use! [
|
|
18
18
|
Minitest::Reporters::SpecReporter.new
|
19
19
|
]
|
20
20
|
|
21
|
+
class ::Fiber
|
22
|
+
attr_writer :auto_watcher
|
23
|
+
end
|
24
|
+
|
21
25
|
class MiniTest::Test
|
22
26
|
def setup
|
27
|
+
# puts "* setup #{self.name}"
|
23
28
|
if Fiber.current.children.size > 0
|
24
29
|
puts "Children left: #{Fiber.current.children.inspect}"
|
25
30
|
exit!
|
26
31
|
end
|
27
32
|
Fiber.current.setup_main_fiber
|
33
|
+
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
34
|
+
Thread.current.agent = Polyphony::LibevAgent.new
|
28
35
|
sleep 0
|
29
36
|
end
|
30
37
|
|
31
38
|
def teardown
|
39
|
+
# puts "* teardown #{self.name.inspect} Fiber.current: #{Fiber.current.inspect}"
|
32
40
|
Fiber.current.terminate_all_children
|
33
41
|
Fiber.current.await_all_children
|
42
|
+
Fiber.current.auto_watcher = nil
|
43
|
+
rescue => e
|
44
|
+
puts e
|
45
|
+
puts e.backtrace.join("\n")
|
46
|
+
exit!
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
@@ -40,4 +53,4 @@ module Kernel
|
|
40
53
|
rescue Exception => e
|
41
54
|
e
|
42
55
|
end
|
43
|
-
end
|
56
|
+
end
|
data/test/test_agent.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class AgentTest < MiniTest::Test
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
@prev_agent = Thread.current.agent
|
9
|
+
@agent = Polyphony::LibevAgent.new
|
10
|
+
Thread.current.agent = @agent
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@agent.finalize
|
15
|
+
Thread.current.agent = @prev_agent
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_sleep
|
19
|
+
count = 0
|
20
|
+
t0 = Time.now
|
21
|
+
spin {
|
22
|
+
@agent.sleep 0.01
|
23
|
+
count += 1
|
24
|
+
}
|
25
|
+
suspend
|
26
|
+
assert Time.now - t0 >= 0.01
|
27
|
+
assert_equal 1, count
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_write_read_partial
|
31
|
+
i, o = IO.pipe
|
32
|
+
buf = +''
|
33
|
+
f = spin { @agent.read(i, buf, 5, false) }
|
34
|
+
@agent.write(o, 'Hello world')
|
35
|
+
return_value = f.await
|
36
|
+
|
37
|
+
assert_equal 'Hello', buf
|
38
|
+
assert_equal return_value, buf
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_write_read_to_eof_limited_buffer
|
42
|
+
i, o = IO.pipe
|
43
|
+
buf = +''
|
44
|
+
f = spin { @agent.read(i, buf, 5, true) }
|
45
|
+
@agent.write(o, 'Hello')
|
46
|
+
snooze
|
47
|
+
@agent.write(o, ' world')
|
48
|
+
snooze
|
49
|
+
o.close
|
50
|
+
return_value = f.await
|
51
|
+
|
52
|
+
assert_equal 'Hello', buf
|
53
|
+
assert_equal return_value, buf
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_write_read_to_eof
|
57
|
+
i, o = IO.pipe
|
58
|
+
buf = +''
|
59
|
+
f = spin { @agent.read(i, buf, 10**6, true) }
|
60
|
+
@agent.write(o, 'Hello')
|
61
|
+
snooze
|
62
|
+
@agent.write(o, ' world')
|
63
|
+
snooze
|
64
|
+
o.close
|
65
|
+
return_value = f.await
|
66
|
+
|
67
|
+
assert_equal 'Hello world', buf
|
68
|
+
assert_equal return_value, buf
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_waitpid
|
72
|
+
pid = fork do
|
73
|
+
@agent.post_fork
|
74
|
+
exit(42)
|
75
|
+
end
|
76
|
+
|
77
|
+
result = @agent.waitpid(pid)
|
78
|
+
assert_equal [pid, 42], result
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_read_loop
|
82
|
+
i, o = IO.pipe
|
83
|
+
|
84
|
+
buf = []
|
85
|
+
spin do
|
86
|
+
buf << :ready
|
87
|
+
@agent.read_loop(i) { |d| buf << d }
|
88
|
+
buf << :done
|
89
|
+
end
|
90
|
+
|
91
|
+
o << 'foo'
|
92
|
+
o << 'bar'
|
93
|
+
o.close
|
94
|
+
snooze
|
95
|
+
|
96
|
+
assert_equal [:ready, 'foo', 'bar', :done], buf
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_accept_loop
|
100
|
+
server = TCPServer.new('127.0.0.1', 1234)
|
101
|
+
|
102
|
+
clients = []
|
103
|
+
server_fiber = spin do
|
104
|
+
@agent.accept_loop(server) { |c| clients << c }
|
105
|
+
end
|
106
|
+
|
107
|
+
c1 = TCPSocket.new('127.0.0.1', 1234)
|
108
|
+
snooze
|
109
|
+
|
110
|
+
assert_equal 1, clients.size
|
111
|
+
|
112
|
+
c2 = TCPSocket.new('127.0.0.1', 1234)
|
113
|
+
snooze
|
114
|
+
|
115
|
+
assert_equal 2, clients.size
|
116
|
+
|
117
|
+
ensure
|
118
|
+
c1&.close
|
119
|
+
c2&.close
|
120
|
+
server_fiber.stop
|
121
|
+
snooze
|
122
|
+
server&.close
|
123
|
+
end
|
124
|
+
end
|