polyphony 0.43.8 → 0.45.0
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/.rubocop.yml +7 -1
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +13 -11
- data/README.md +20 -5
- data/Rakefile +1 -1
- data/TODO.md +16 -14
- data/bin/stress.rb +28 -0
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-pry.rb +18 -0
- data/examples/io/xx-rack_server.rb +71 -0
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/ext/polyphony/backend.h +41 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +234 -228
- data/ext/polyphony/polyphony.c +4 -0
- data/ext/polyphony/polyphony.h +16 -16
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +52 -12
- data/ext/polyphony/thread.c +55 -42
- data/lib/polyphony.rb +25 -39
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +19 -0
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +11 -6
- data/lib/polyphony/core/resource_pool.rb +22 -71
- data/lib/polyphony/core/sync.rb +48 -9
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +37 -19
- data/lib/polyphony/extensions/fiber.rb +5 -1
- data/lib/polyphony/extensions/io.rb +7 -8
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +12 -22
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_event.rb +1 -0
- data/test/test_fiber.rb +21 -5
- data/test/test_io.rb +1 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_queue.rb +20 -0
- data/test/test_resource_pool.rb +34 -43
- data/test/test_signal.rb +5 -29
- data/test/test_sync.rb +52 -0
- metadata +74 -30
- data/.gitbook.yaml +0 -4
- data/lib/polyphony/event.rb +0 -17
@@ -46,6 +46,10 @@ class ::Exception
|
|
46
46
|
|
47
47
|
backtrace.reject { |l| l[POLYPHONY_DIR] }
|
48
48
|
end
|
49
|
+
|
50
|
+
def invoke
|
51
|
+
Kernel.raise(self)
|
52
|
+
end
|
49
53
|
end
|
50
54
|
|
51
55
|
# Overrides for Process
|
@@ -53,7 +57,7 @@ module ::Process
|
|
53
57
|
class << self
|
54
58
|
alias_method :orig_detach, :detach
|
55
59
|
def detach(pid)
|
56
|
-
fiber = spin { Thread.current.
|
60
|
+
fiber = spin { Thread.current.backend.waitpid(pid) }
|
57
61
|
fiber.define_singleton_method(:pid) { pid }
|
58
62
|
fiber
|
59
63
|
end
|
@@ -112,19 +116,28 @@ module ::Kernel
|
|
112
116
|
strs = args.inject([]) do |m, a|
|
113
117
|
m << a.inspect << "\n"
|
114
118
|
end
|
115
|
-
STDOUT.write
|
119
|
+
STDOUT.write(*strs)
|
116
120
|
args.size == 1 ? args.first : args
|
117
121
|
end
|
118
122
|
|
119
123
|
alias_method :orig_system, :system
|
120
124
|
def system(*args)
|
121
|
-
|
122
|
-
|
123
|
-
|
125
|
+
Kernel.system(*args)
|
126
|
+
end
|
127
|
+
|
128
|
+
class << self
|
129
|
+
alias_method :orig_system, :system
|
130
|
+
def system(*args)
|
131
|
+
waiter = nil
|
132
|
+
Open3.popen2(*args) do |i, o, t|
|
133
|
+
waiter = t
|
134
|
+
i.close
|
135
|
+
pipe_to_eof(o, $stdout)
|
136
|
+
end
|
137
|
+
waiter.await.last == 0
|
138
|
+
rescue SystemCallError
|
139
|
+
nil
|
124
140
|
end
|
125
|
-
true
|
126
|
-
rescue SystemCallError
|
127
|
-
nil
|
128
141
|
end
|
129
142
|
|
130
143
|
def pipe_to_eof(src, dest)
|
@@ -139,29 +152,34 @@ module ::Kernel
|
|
139
152
|
alias_method :orig_trap, :trap
|
140
153
|
def trap(sig, command = nil, &block)
|
141
154
|
return orig_trap(sig, command) if command.is_a? String
|
142
|
-
|
143
|
-
block = command if command.respond_to?(:call)
|
144
|
-
exception =
|
155
|
+
|
156
|
+
block = command if !block && command.respond_to?(:call)
|
157
|
+
exception = signal_exception(block, command)
|
145
158
|
|
146
159
|
# The signal trap can be invoked at any time, including while the system
|
147
|
-
#
|
160
|
+
# backend is blocking while polling for events. In order to deal with this
|
148
161
|
# correctly, we spin a fiber that will run the signal handler code, then
|
149
162
|
# call break_out_of_ev_loop, which will put the fiber at the front of the
|
150
|
-
# run queue, then wake up the
|
163
|
+
# run queue, then wake up the backend.
|
151
164
|
#
|
152
165
|
# If the command argument is an exception class however, it will be raised
|
153
166
|
# directly in the context of the main fiber.
|
154
167
|
orig_trap(sig) do
|
155
|
-
|
156
|
-
Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, exception)
|
157
|
-
else
|
158
|
-
fiber = spin { snooze; block.call }
|
159
|
-
Thread.current.break_out_of_ev_loop(fiber, nil)
|
160
|
-
end
|
168
|
+
Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, exception)
|
161
169
|
end
|
162
170
|
end
|
163
171
|
end
|
164
172
|
|
173
|
+
def signal_exception(block, command)
|
174
|
+
if block
|
175
|
+
Polyphony::Interjection.new(block)
|
176
|
+
elsif command.is_a?(Class)
|
177
|
+
command.new
|
178
|
+
else
|
179
|
+
raise ArgumentError, 'Must supply block or exception or callable object'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
165
183
|
# Override Timeout to use cancel scope
|
166
184
|
module ::Timeout
|
167
185
|
def self.timeout(sec, klass = nil, message = nil, &block)
|
@@ -67,6 +67,10 @@ module Polyphony
|
|
67
67
|
else RuntimeError.new
|
68
68
|
end
|
69
69
|
end
|
70
|
+
|
71
|
+
def interject(&block)
|
72
|
+
raise Polyphony::Interjection.new(block)
|
73
|
+
end
|
70
74
|
end
|
71
75
|
|
72
76
|
# Fiber supervision
|
@@ -224,7 +228,7 @@ module Polyphony
|
|
224
228
|
@results = @children.dup
|
225
229
|
@on_child_done = proc do |c, r|
|
226
230
|
@results[c] = r
|
227
|
-
|
231
|
+
schedule if @children.empty?
|
228
232
|
end
|
229
233
|
suspend
|
230
234
|
@on_child_done = nil
|
@@ -99,7 +99,7 @@ class ::IO
|
|
99
99
|
alias_method :orig_read, :read
|
100
100
|
def read(len = nil)
|
101
101
|
@read_buffer ||= +''
|
102
|
-
result = Thread.current.
|
102
|
+
result = Thread.current.backend.read(self, @read_buffer, len, true)
|
103
103
|
return nil unless result
|
104
104
|
|
105
105
|
already_read = @read_buffer
|
@@ -110,7 +110,7 @@ class ::IO
|
|
110
110
|
alias_method :orig_readpartial, :read
|
111
111
|
def readpartial(len, str = nil)
|
112
112
|
@read_buffer ||= +''
|
113
|
-
result = Thread.current.
|
113
|
+
result = Thread.current.backend.read(self, @read_buffer, len, false)
|
114
114
|
raise EOFError unless result
|
115
115
|
|
116
116
|
if str
|
@@ -124,12 +124,12 @@ class ::IO
|
|
124
124
|
|
125
125
|
alias_method :orig_write, :write
|
126
126
|
def write(str, *args)
|
127
|
-
Thread.current.
|
127
|
+
Thread.current.backend.write(self, str, *args)
|
128
128
|
end
|
129
129
|
|
130
130
|
alias_method :orig_write_chevron, :<<
|
131
131
|
def <<(str)
|
132
|
-
Thread.current.
|
132
|
+
Thread.current.backend.write(self, str)
|
133
133
|
self
|
134
134
|
end
|
135
135
|
|
@@ -170,13 +170,12 @@ class ::IO
|
|
170
170
|
return
|
171
171
|
end
|
172
172
|
|
173
|
-
strs = args.
|
173
|
+
strs = args.each_with_object([]) do |a, m|
|
174
174
|
a = a.to_s
|
175
175
|
m << a
|
176
176
|
m << "\n" unless a =~ /\n$/
|
177
|
-
m
|
178
177
|
end
|
179
|
-
write
|
178
|
+
write(*strs)
|
180
179
|
nil
|
181
180
|
end
|
182
181
|
|
@@ -203,7 +202,7 @@ class ::IO
|
|
203
202
|
end
|
204
203
|
|
205
204
|
def read_loop(&block)
|
206
|
-
Thread.current.
|
205
|
+
Thread.current.backend.read_loop(self, &block)
|
207
206
|
end
|
208
207
|
|
209
208
|
# alias_method :orig_read, :read
|
@@ -28,8 +28,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
28
28
|
loop do
|
29
29
|
result = accept_nonblock(exception: false)
|
30
30
|
case result
|
31
|
-
when :wait_readable then Thread.current.
|
32
|
-
when :wait_writable then Thread.current.
|
31
|
+
when :wait_readable then Thread.current.backend.wait_io(io, false)
|
32
|
+
when :wait_writable then Thread.current.backend.wait_io(io, true)
|
33
33
|
else
|
34
34
|
return result
|
35
35
|
end
|
@@ -40,8 +40,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
40
40
|
def sysread(maxlen, buf = +'')
|
41
41
|
loop do
|
42
42
|
case (result = read_nonblock(maxlen, buf, exception: false))
|
43
|
-
when :wait_readable then Thread.current.
|
44
|
-
when :wait_writable then Thread.current.
|
43
|
+
when :wait_readable then Thread.current.backend.wait_io(io, false)
|
44
|
+
when :wait_writable then Thread.current.backend.wait_io(io, true)
|
45
45
|
else return result
|
46
46
|
end
|
47
47
|
end
|
@@ -51,8 +51,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
51
51
|
def syswrite(buf)
|
52
52
|
loop do
|
53
53
|
case (result = write_nonblock(buf, exception: false))
|
54
|
-
when :wait_readable then Thread.current.
|
55
|
-
when :wait_writable then Thread.current.
|
54
|
+
when :wait_readable then Thread.current.backend.wait_io(io, false)
|
55
|
+
when :wait_writable then Thread.current.backend.wait_io(io, true)
|
56
56
|
else
|
57
57
|
return result
|
58
58
|
end
|
@@ -5,34 +5,16 @@ require 'socket'
|
|
5
5
|
require_relative './io'
|
6
6
|
require_relative '../core/thread_pool'
|
7
7
|
|
8
|
-
class ::BasicSocket
|
9
|
-
def write_nonblock(string, _options = {})
|
10
|
-
write(string)
|
11
|
-
end
|
12
|
-
|
13
|
-
def read_nonblock(maxlen, str = nil, _options = {})
|
14
|
-
readpartial(maxlen, str)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
8
|
# Socket overrides (eventually rewritten in C)
|
19
9
|
class ::Socket
|
20
10
|
def accept
|
21
|
-
Thread.current.
|
11
|
+
Thread.current.backend.accept(self)
|
22
12
|
end
|
23
13
|
|
24
14
|
NO_EXCEPTION = { exception: false }.freeze
|
25
15
|
|
26
16
|
def connect(remotesockaddr)
|
27
|
-
|
28
|
-
result = connect_nonblock(remotesockaddr, **NO_EXCEPTION)
|
29
|
-
case result
|
30
|
-
when 0 then return
|
31
|
-
when :wait_writable then Thread.current.agent.wait_io(self, true)
|
32
|
-
else
|
33
|
-
raise IOError
|
34
|
-
end
|
35
|
-
end
|
17
|
+
Thread.current.backend.connect(self, remotesockaddr.ip_address, remotesockaddr.ip_port)
|
36
18
|
end
|
37
19
|
|
38
20
|
def recv(maxlen, flags = 0, outbuf = nil)
|
@@ -41,7 +23,7 @@ class ::Socket
|
|
41
23
|
result = recv_nonblock(maxlen, flags, outbuf, **NO_EXCEPTION)
|
42
24
|
case result
|
43
25
|
when nil then raise IOError
|
44
|
-
when :wait_readable then Thread.current.
|
26
|
+
when :wait_readable then Thread.current.backend.wait_io(self, false)
|
45
27
|
else
|
46
28
|
return result
|
47
29
|
end
|
@@ -54,7 +36,7 @@ class ::Socket
|
|
54
36
|
result = recvfrom_nonblock(maxlen, flags, @read_buffer, **NO_EXCEPTION)
|
55
37
|
case result
|
56
38
|
when nil then raise IOError
|
57
|
-
when :wait_readable then Thread.current.
|
39
|
+
when :wait_readable then Thread.current.backend.wait_io(self, false)
|
58
40
|
else
|
59
41
|
return result
|
60
42
|
end
|
@@ -75,6 +57,10 @@ class ::Socket
|
|
75
57
|
setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
76
58
|
end
|
77
59
|
|
60
|
+
def reuse_port
|
61
|
+
setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
|
62
|
+
end
|
63
|
+
|
78
64
|
class << self
|
79
65
|
alias_method :orig_getaddrinfo, :getaddrinfo
|
80
66
|
def getaddrinfo(*args)
|
@@ -128,6 +114,10 @@ class ::TCPSocket
|
|
128
114
|
def reuse_addr
|
129
115
|
setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
|
130
116
|
end
|
117
|
+
|
118
|
+
def reuse_port
|
119
|
+
setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
|
120
|
+
end
|
131
121
|
end
|
132
122
|
|
133
123
|
# Override stock TCPServer code by encapsulating a Socket instance.
|
@@ -16,21 +16,22 @@ class ::Thread
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def execute
|
19
|
-
#
|
19
|
+
# backend must be created in the context of the new thread, therefore it
|
20
20
|
# cannot be created in Thread#initialize
|
21
|
-
@
|
21
|
+
@backend = Polyphony::Backend.new
|
22
22
|
setup
|
23
23
|
@ready = true
|
24
24
|
result = @block.(*@args)
|
25
25
|
rescue Polyphony::MoveOn, Polyphony::Terminate => e
|
26
26
|
result = e.value
|
27
|
-
rescue Exception =>
|
27
|
+
rescue Exception => e
|
28
|
+
result = e
|
28
29
|
ensure
|
29
30
|
@ready = true
|
30
31
|
finalize(result)
|
31
32
|
end
|
32
33
|
|
33
|
-
attr_accessor :
|
34
|
+
attr_accessor :backend
|
34
35
|
|
35
36
|
def setup
|
36
37
|
@main_fiber = Fiber.current
|
@@ -48,7 +49,7 @@ class ::Thread
|
|
48
49
|
@result = result
|
49
50
|
signal_waiters(result)
|
50
51
|
end
|
51
|
-
@
|
52
|
+
@backend.finalize
|
52
53
|
end
|
53
54
|
|
54
55
|
def signal_waiters(result)
|
data/lib/polyphony/net.rb
CHANGED
@@ -35,9 +35,10 @@ module Polyphony
|
|
35
35
|
::Socket.new(:INET, :STREAM).tap do |s|
|
36
36
|
s.reuse_addr if opts[:reuse_addr]
|
37
37
|
s.dont_linger if opts[:dont_linger]
|
38
|
+
s.reuse_port if opts[:reuse_port]
|
38
39
|
addr = ::Socket.sockaddr_in(port, host)
|
39
40
|
s.bind(addr)
|
40
|
-
s.listen(
|
41
|
+
s.listen(opts[:backlog] || Socket::SOMAXCONN)
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
data/lib/polyphony/version.rb
CHANGED
data/polyphony.gemspec
CHANGED
@@ -21,17 +21,20 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.required_ruby_version = '>= 2.6'
|
23
23
|
|
24
|
-
s.add_development_dependency '
|
25
|
-
s.add_development_dependency 'localhost', '1.1.4'
|
24
|
+
s.add_development_dependency 'rake-compiler', '1.0.5'
|
26
25
|
s.add_development_dependency 'minitest', '5.13.0'
|
27
26
|
s.add_development_dependency 'minitest-reporters', '1.4.2'
|
28
27
|
s.add_development_dependency 'simplecov', '0.17.1'
|
29
28
|
s.add_development_dependency 'rubocop', '0.85.1'
|
29
|
+
s.add_development_dependency 'pry', '0.13.1'
|
30
|
+
|
30
31
|
s.add_development_dependency 'pg', '1.1.4'
|
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'
|
34
34
|
s.add_development_dependency 'http_parser.rb', '~>0.6.0'
|
35
|
+
s.add_development_dependency 'rack', '>=2.0.8', '<2.3.0'
|
36
|
+
s.add_development_dependency 'mysql2', '0.5.3'
|
37
|
+
s.add_development_dependency 'sequel', '5.34.0'
|
35
38
|
|
36
39
|
s.add_development_dependency 'jekyll', '~>3.8.6'
|
37
40
|
s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
|
data/test/helper.rb
CHANGED
@@ -31,7 +31,7 @@ class MiniTest::Test
|
|
31
31
|
end
|
32
32
|
Fiber.current.setup_main_fiber
|
33
33
|
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
34
|
-
Thread.current.
|
34
|
+
Thread.current.backend = Polyphony::Backend.new
|
35
35
|
sleep 0 # apparently this helps with timer accuracy
|
36
36
|
end
|
37
37
|
|
@@ -2,39 +2,39 @@
|
|
2
2
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
|
-
class
|
5
|
+
class BackendTest < MiniTest::Test
|
6
6
|
def setup
|
7
7
|
super
|
8
|
-
@
|
9
|
-
@
|
10
|
-
Thread.current.
|
8
|
+
@prev_backend = Thread.current.backend
|
9
|
+
@backend = Polyphony::Backend.new
|
10
|
+
Thread.current.backend = @backend
|
11
11
|
end
|
12
12
|
|
13
13
|
def teardown
|
14
|
-
@
|
15
|
-
Thread.current.
|
14
|
+
@backend.finalize
|
15
|
+
Thread.current.backend = @prev_backend
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_sleep
|
19
19
|
count = 0
|
20
20
|
t0 = Time.now
|
21
21
|
spin {
|
22
|
-
@
|
22
|
+
@backend.sleep 0.01
|
23
23
|
count += 1
|
24
|
-
@
|
24
|
+
@backend.sleep 0.01
|
25
25
|
count += 1
|
26
|
-
@
|
26
|
+
@backend.sleep 0.01
|
27
27
|
count += 1
|
28
28
|
}.await
|
29
|
-
|
29
|
+
assert_in_delta 0.03, Time.now - t0, 0.005
|
30
30
|
assert_equal 3, count
|
31
31
|
end
|
32
32
|
|
33
33
|
def test_write_read_partial
|
34
34
|
i, o = IO.pipe
|
35
35
|
buf = +''
|
36
|
-
f = spin { @
|
37
|
-
@
|
36
|
+
f = spin { @backend.read(i, buf, 5, false) }
|
37
|
+
@backend.write(o, 'Hello world')
|
38
38
|
return_value = f.await
|
39
39
|
|
40
40
|
assert_equal 'Hello', buf
|
@@ -44,10 +44,10 @@ class AgentTest < MiniTest::Test
|
|
44
44
|
def test_write_read_to_eof_limited_buffer
|
45
45
|
i, o = IO.pipe
|
46
46
|
buf = +''
|
47
|
-
f = spin { @
|
48
|
-
@
|
47
|
+
f = spin { @backend.read(i, buf, 5, true) }
|
48
|
+
@backend.write(o, 'Hello')
|
49
49
|
snooze
|
50
|
-
@
|
50
|
+
@backend.write(o, ' world')
|
51
51
|
snooze
|
52
52
|
o.close
|
53
53
|
return_value = f.await
|
@@ -59,10 +59,10 @@ class AgentTest < MiniTest::Test
|
|
59
59
|
def test_write_read_to_eof
|
60
60
|
i, o = IO.pipe
|
61
61
|
buf = +''
|
62
|
-
f = spin { @
|
63
|
-
@
|
62
|
+
f = spin { @backend.read(i, buf, 10**6, true) }
|
63
|
+
@backend.write(o, 'Hello')
|
64
64
|
snooze
|
65
|
-
@
|
65
|
+
@backend.write(o, ' world')
|
66
66
|
snooze
|
67
67
|
o.close
|
68
68
|
return_value = f.await
|
@@ -73,11 +73,11 @@ class AgentTest < MiniTest::Test
|
|
73
73
|
|
74
74
|
def test_waitpid
|
75
75
|
pid = fork do
|
76
|
-
@
|
76
|
+
@backend.post_fork
|
77
77
|
exit(42)
|
78
78
|
end
|
79
79
|
|
80
|
-
result = @
|
80
|
+
result = @backend.waitpid(pid)
|
81
81
|
assert_equal [pid, 42], result
|
82
82
|
end
|
83
83
|
|
@@ -87,7 +87,7 @@ class AgentTest < MiniTest::Test
|
|
87
87
|
buf = []
|
88
88
|
spin do
|
89
89
|
buf << :ready
|
90
|
-
@
|
90
|
+
@backend.read_loop(i) { |d| buf << d }
|
91
91
|
buf << :done
|
92
92
|
end
|
93
93
|
|
@@ -107,7 +107,7 @@ class AgentTest < MiniTest::Test
|
|
107
107
|
|
108
108
|
clients = []
|
109
109
|
server_fiber = spin do
|
110
|
-
@
|
110
|
+
@backend.accept_loop(server) { |c| clients << c }
|
111
111
|
end
|
112
112
|
|
113
113
|
c1 = TCPSocket.new('127.0.0.1', 1234)
|