polyphony 0.21 → 0.22
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 +1 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +5 -5
- data/TODO.md +83 -20
- data/docs/technical-overview/design-principles.md +3 -3
- data/docs/technical-overview/faq.md +11 -0
- data/docs/technical-overview/fiber-scheduling.md +46 -33
- data/examples/core/sleep_spin.rb +2 -2
- data/examples/http/http2_raw.rb +135 -0
- data/examples/http/http_client.rb +14 -3
- data/examples/http/http_get.rb +28 -2
- data/examples/http/http_server.rb +3 -1
- data/examples/http/http_server_forked.rb +3 -1
- data/examples/interfaces/pg_pool.rb +1 -0
- data/examples/io/echo_server.rb +1 -0
- data/ext/gyro/async.c +7 -9
- data/ext/gyro/child.c +5 -8
- data/ext/gyro/extconf.rb +2 -0
- data/ext/gyro/gyro.c +159 -204
- data/ext/gyro/gyro.h +16 -6
- data/ext/gyro/io.c +7 -10
- data/ext/gyro/signal.c +3 -0
- data/ext/gyro/timer.c +9 -18
- data/lib/polyphony/auto_run.rb +12 -5
- data/lib/polyphony/core/coprocess.rb +1 -1
- data/lib/polyphony/core/resource_pool.rb +49 -15
- data/lib/polyphony/core/supervisor.rb +3 -2
- data/lib/polyphony/extensions/core.rb +16 -3
- data/lib/polyphony/extensions/io.rb +2 -0
- data/lib/polyphony/extensions/openssl.rb +60 -0
- data/lib/polyphony/extensions/socket.rb +0 -4
- data/lib/polyphony/http/client/agent.rb +127 -0
- data/lib/polyphony/http/client/http1.rb +129 -0
- data/lib/polyphony/http/client/http2.rb +180 -0
- data/lib/polyphony/http/client/response.rb +32 -0
- data/lib/polyphony/http/client/site_connection_manager.rb +109 -0
- data/lib/polyphony/http/server/request.rb +0 -1
- data/lib/polyphony/http.rb +1 -1
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +4 -4
- data/polyphony.gemspec +1 -0
- data/test/test_gyro.rb +42 -10
- data/test/test_resource_pool.rb +107 -0
- metadata +10 -4
- data/lib/polyphony/http/agent.rb +0 -250
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
export_default :HTTP2Adapter
|
4
|
+
|
5
|
+
require 'http/2'
|
6
|
+
|
7
|
+
Response = import './response'
|
8
|
+
|
9
|
+
# HTTP 2 adapter
|
10
|
+
class HTTP2Adapter
|
11
|
+
def initialize(socket)
|
12
|
+
@socket = socket
|
13
|
+
@client = HTTP2::Client.new
|
14
|
+
@client.on(:frame) { |bytes| socket << bytes }
|
15
|
+
# @client.on(:frame_received) do |frame|
|
16
|
+
# puts "Received frame: #{frame.inspect}"
|
17
|
+
# end
|
18
|
+
# @client.on(:frame_sent) do |frame|
|
19
|
+
# puts "Sent frame: #{frame.inspect}"
|
20
|
+
# end
|
21
|
+
|
22
|
+
@reader = spin do
|
23
|
+
while (data = socket.readpartial(8192))
|
24
|
+
@client << data
|
25
|
+
snooze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def allocate_stream_adapter
|
31
|
+
StreamAdapter.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def allocate_stream
|
35
|
+
@client.new_stream
|
36
|
+
end
|
37
|
+
|
38
|
+
def protocol
|
39
|
+
:http2
|
40
|
+
end
|
41
|
+
|
42
|
+
# Virtualizes adapter over HTTP2 stream
|
43
|
+
class StreamAdapter
|
44
|
+
def initialize(connection)
|
45
|
+
@connection = connection
|
46
|
+
end
|
47
|
+
|
48
|
+
def request(ctx)
|
49
|
+
stream = setup_stream # (ctx, stream)
|
50
|
+
send_request(ctx, stream)
|
51
|
+
|
52
|
+
stream.on(:headers, &method(:on_headers))
|
53
|
+
stream.on(:data, &method(:on_data))
|
54
|
+
stream.on(:close, &method(:on_close))
|
55
|
+
|
56
|
+
# stream.on(:active) { puts "* active" }
|
57
|
+
# stream.on(:half_close) { puts "* half_close" }
|
58
|
+
|
59
|
+
wait_for_response(ctx, stream)
|
60
|
+
rescue Exception => e
|
61
|
+
p e
|
62
|
+
puts e.backtrace.join("\n")
|
63
|
+
# ensure
|
64
|
+
# stream.close
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_request(ctx, stream)
|
68
|
+
headers = prepare_headers(ctx)
|
69
|
+
if ctx[:opts][:payload]
|
70
|
+
stream.headers(headers, end_stream: false)
|
71
|
+
stream.data(ctx[:opts][:payload], end_stream: true)
|
72
|
+
else
|
73
|
+
stream.headers(headers, end_stream: true)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_headers(headers)
|
78
|
+
if @waiting_headers_fiber
|
79
|
+
@waiting_headers_fiber.schedule headers.to_h
|
80
|
+
else
|
81
|
+
@headers = headers.to_h
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def on_data(chunk)
|
86
|
+
if @waiting_chunk_fiber
|
87
|
+
@waiting_chunk_fiber&.schedule chunk
|
88
|
+
else
|
89
|
+
@buffered_chunks << chunk
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def on_close(_stream)
|
94
|
+
@done = true
|
95
|
+
@waiting_done_fiber&.schedule
|
96
|
+
end
|
97
|
+
|
98
|
+
def setup_stream
|
99
|
+
stream = @connection.allocate_stream
|
100
|
+
|
101
|
+
@headers = nil
|
102
|
+
@done = nil
|
103
|
+
@buffered_chunks = []
|
104
|
+
|
105
|
+
@waiting_headers_fiber = nil
|
106
|
+
@waiting_chunk_fiber = nil
|
107
|
+
@waiting_done_fiber = nil
|
108
|
+
|
109
|
+
stream
|
110
|
+
end
|
111
|
+
|
112
|
+
def wait_for_response(_ctx, _stream)
|
113
|
+
headers = wait_for_headers
|
114
|
+
Response.new(self, headers[':status'].to_i, headers)
|
115
|
+
end
|
116
|
+
|
117
|
+
def wait_for_headers
|
118
|
+
return @headers if @headers
|
119
|
+
|
120
|
+
@waiting_headers_fiber = Fiber.current
|
121
|
+
suspend
|
122
|
+
end
|
123
|
+
|
124
|
+
def protocol
|
125
|
+
:http2
|
126
|
+
end
|
127
|
+
|
128
|
+
def prepare_headers(ctx)
|
129
|
+
headers = {
|
130
|
+
':method' => ctx[:method].to_s,
|
131
|
+
':scheme' => ctx[:uri].scheme,
|
132
|
+
':authority' => [ctx[:uri].host, ctx[:uri].port].join(':'),
|
133
|
+
':path' => ctx[:uri].request_uri,
|
134
|
+
'User-Agent' => 'curl/7.54.0'
|
135
|
+
}
|
136
|
+
headers.merge!(ctx[:opts][:headers]) if ctx[:opts][:headers]
|
137
|
+
headers
|
138
|
+
end
|
139
|
+
|
140
|
+
def body
|
141
|
+
@waiting_done_fiber = Fiber.current
|
142
|
+
suspend
|
143
|
+
@buffered_chunks.join
|
144
|
+
# body = +''
|
145
|
+
# while !@done
|
146
|
+
# p :body_suspend_pre
|
147
|
+
# chunk = suspend
|
148
|
+
# p :body_suspend_post
|
149
|
+
# body << chunk
|
150
|
+
# end
|
151
|
+
# puts ""
|
152
|
+
# body
|
153
|
+
rescue Exception => e
|
154
|
+
p e
|
155
|
+
puts e.backtrace.join("\n")
|
156
|
+
end
|
157
|
+
|
158
|
+
def each_chunk
|
159
|
+
yield @buffered_chunks.shift until @buffered_chunks.empty?
|
160
|
+
|
161
|
+
@waiting_chunk_fiber = Fiber.current
|
162
|
+
until @done
|
163
|
+
chunk = suspend
|
164
|
+
yield chunk
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def next_body_chunk
|
169
|
+
return yield @buffered_chunks.shift unless @buffered_chunks.empty?
|
170
|
+
|
171
|
+
@waiting_chunk_fuber = Fiber.current
|
172
|
+
until @done
|
173
|
+
chunk = suspend
|
174
|
+
return yield chunk
|
175
|
+
end
|
176
|
+
|
177
|
+
nil
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
export_default :Response
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
# HTTP response
|
8
|
+
class Response
|
9
|
+
attr_reader :status_code, :headers
|
10
|
+
|
11
|
+
def initialize(adapter, status_code, headers)
|
12
|
+
@adapter = adapter
|
13
|
+
@status_code = status_code
|
14
|
+
@headers = headers
|
15
|
+
end
|
16
|
+
|
17
|
+
def body
|
18
|
+
@body ||= @adapter.body
|
19
|
+
end
|
20
|
+
|
21
|
+
def each_chunk(&block)
|
22
|
+
@adapter.each_chunk(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def next_body_chunk
|
26
|
+
@adapter.next_body_chunk
|
27
|
+
end
|
28
|
+
|
29
|
+
def json
|
30
|
+
@json ||= ::JSON.parse(body)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
export_default :SiteConnectionManager
|
4
|
+
|
5
|
+
ResourcePool = import '../../core/resource_pool'
|
6
|
+
HTTP1Adapter = import './http1'
|
7
|
+
HTTP2Adapter = import './http2'
|
8
|
+
|
9
|
+
# HTTP site connection pool
|
10
|
+
class SiteConnectionManager < ResourcePool
|
11
|
+
def initialize(uri_key)
|
12
|
+
@uri_key = uri_key
|
13
|
+
super(limit: 4)
|
14
|
+
end
|
15
|
+
|
16
|
+
# def method_missing(sym, *args)
|
17
|
+
# raise "Invalid method #{sym}"
|
18
|
+
# end
|
19
|
+
|
20
|
+
def acquire
|
21
|
+
Gyro.ref
|
22
|
+
prepare_first_connection if @size.zero?
|
23
|
+
super
|
24
|
+
ensure
|
25
|
+
Gyro.unref
|
26
|
+
# The size goes back to 0 only in case existing connections get into an
|
27
|
+
# error state and then get discarded
|
28
|
+
@state = nil if @size == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def prepare_first_connection
|
32
|
+
case @state
|
33
|
+
when nil
|
34
|
+
@state = :first_connection
|
35
|
+
create_first_connection
|
36
|
+
when :first_connection
|
37
|
+
@first_connection_queue << Fiber.current
|
38
|
+
suspend
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_first_connection
|
43
|
+
@first_connection_queue = []
|
44
|
+
# @first_connection_queue << Fiber.current
|
45
|
+
|
46
|
+
adapter = connect
|
47
|
+
@state = adapter.protocol
|
48
|
+
send(:"setup_#{@state}_allocator", adapter)
|
49
|
+
dequeue_first_connection_waiters
|
50
|
+
end
|
51
|
+
|
52
|
+
def setup_http1_allocator(adapter)
|
53
|
+
@size += 1
|
54
|
+
adapter.extend ResourceExtensions
|
55
|
+
@stock << adapter
|
56
|
+
@allocator = proc { connect }
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup_http2_allocator(adapter)
|
60
|
+
@adapter = adapter
|
61
|
+
@limit = 20
|
62
|
+
@size += 1
|
63
|
+
stream_adapter = adapter.allocate_stream_adapter
|
64
|
+
stream_adapter.extend ResourceExtensions
|
65
|
+
@stock << stream_adapter
|
66
|
+
@allocator = proc { adapter.allocate_stream_adapter }
|
67
|
+
end
|
68
|
+
|
69
|
+
def dequeue_first_connection_waiters
|
70
|
+
return unless @first_connection_queue
|
71
|
+
|
72
|
+
@first_connection_queue.each(&:schedule)
|
73
|
+
@first_connection_queue = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def connect
|
77
|
+
socket = create_socket
|
78
|
+
protocol = socket_protocol(socket)
|
79
|
+
case protocol
|
80
|
+
when :http1
|
81
|
+
HTTP1Adapter.new(socket)
|
82
|
+
when :http2
|
83
|
+
HTTP2Adapter.new(socket)
|
84
|
+
else
|
85
|
+
raise "Unknown protocol #{protocol.inspect}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def socket_protocol(socket)
|
90
|
+
if socket.is_a?(OpenSSL::SSL::SSLSocket) && socket.alpn_protocol == 'h2'
|
91
|
+
:http2
|
92
|
+
else
|
93
|
+
:http1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
SECURE_OPTS = { secure: true, alpn_protocols: ['h2', 'http/1.1'] }.freeze
|
98
|
+
|
99
|
+
def create_socket
|
100
|
+
case @uri_key[:scheme]
|
101
|
+
when 'http'
|
102
|
+
Polyphony::Net.tcp_connect(@uri_key[:host], @uri_key[:port])
|
103
|
+
when 'https'
|
104
|
+
Polyphony::Net.tcp_connect(@uri_key[:host], @uri_key[:port], SECURE_OPTS)
|
105
|
+
else
|
106
|
+
raise "Invalid scheme #{@uri_key[:scheme].inspect}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/polyphony/http.rb
CHANGED
data/lib/polyphony/net.rb
CHANGED
@@ -41,7 +41,8 @@ def socket_from_options(host, port, opts)
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def secure_socket(socket, context, opts)
|
44
|
-
|
44
|
+
context ||= OpenSSL::SSL::SSLContext.new
|
45
|
+
setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
|
45
46
|
socket = secure_socket_wrapper(socket, context)
|
46
47
|
|
47
48
|
socket.tap do |s|
|
data/lib/polyphony/version.rb
CHANGED
data/lib/polyphony.rb
CHANGED
@@ -52,7 +52,7 @@ module Polyphony
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def fork(&block)
|
55
|
-
Gyro.break
|
55
|
+
Gyro.break!
|
56
56
|
pid = Kernel.fork do
|
57
57
|
setup_forked_process
|
58
58
|
block.()
|
@@ -64,13 +64,13 @@ module Polyphony
|
|
64
64
|
# exiting.
|
65
65
|
suspend
|
66
66
|
end
|
67
|
-
Gyro.
|
67
|
+
Gyro.reset!
|
68
68
|
pid
|
69
69
|
end
|
70
70
|
|
71
71
|
def reset!
|
72
|
-
Fiber.root.scheduled_value = nil
|
73
|
-
Gyro.
|
72
|
+
# Fiber.root.scheduled_value = nil
|
73
|
+
Gyro.reset!
|
74
74
|
end
|
75
75
|
|
76
76
|
private
|
data/polyphony.gemspec
CHANGED
data/test/test_gyro.rb
CHANGED
@@ -2,7 +2,42 @@
|
|
2
2
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
|
-
class
|
5
|
+
class SchedulingTest < MiniTest::Test
|
6
|
+
def test_fiber_state
|
7
|
+
assert_equal :running, Fiber.current.state
|
8
|
+
|
9
|
+
f = Fiber.new {}
|
10
|
+
|
11
|
+
assert_equal :paused, f.state
|
12
|
+
f.resume
|
13
|
+
assert_equal :dead, f.state
|
14
|
+
|
15
|
+
f = Fiber.new { }
|
16
|
+
f.schedule
|
17
|
+
assert_equal :scheduled, f.state
|
18
|
+
snooze
|
19
|
+
assert_equal :dead, f.state
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_schedule
|
23
|
+
values = []
|
24
|
+
fibers = 3.times.map { |i| Fiber.new { values << i } }
|
25
|
+
fibers[0].schedule
|
26
|
+
|
27
|
+
assert_equal [], values
|
28
|
+
snooze
|
29
|
+
assert_equal [0], values
|
30
|
+
|
31
|
+
fibers[1].schedule
|
32
|
+
fibers[2].schedule
|
33
|
+
|
34
|
+
assert_equal [0], values
|
35
|
+
snooze
|
36
|
+
assert_equal [0, 1, 2], values
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class RunTest < MiniTest::Test
|
6
41
|
def test_that_run_loop_returns_immediately_if_no_watchers
|
7
42
|
t0 = Time.now
|
8
43
|
suspend
|
@@ -88,7 +123,7 @@ class IdleTest < MiniTest::Test
|
|
88
123
|
end.schedule
|
89
124
|
|
90
125
|
Fiber.new do
|
91
|
-
Gyro.break
|
126
|
+
Gyro.break!
|
92
127
|
end.schedule
|
93
128
|
|
94
129
|
suspend
|
@@ -96,7 +131,7 @@ class IdleTest < MiniTest::Test
|
|
96
131
|
assert_equal [:foo], values
|
97
132
|
end
|
98
133
|
|
99
|
-
def
|
134
|
+
def test_reset
|
100
135
|
values = []
|
101
136
|
f1 = Fiber.new do
|
102
137
|
values << :foo
|
@@ -106,7 +141,7 @@ class IdleTest < MiniTest::Test
|
|
106
141
|
end.schedule
|
107
142
|
|
108
143
|
f2 = Fiber.new do
|
109
|
-
Gyro.
|
144
|
+
Gyro.reset!
|
110
145
|
values << :restarted
|
111
146
|
snooze
|
112
147
|
values << :baz
|
@@ -114,12 +149,9 @@ class IdleTest < MiniTest::Test
|
|
114
149
|
|
115
150
|
suspend
|
116
151
|
|
117
|
-
Gyro.start
|
118
|
-
f2.schedule
|
119
152
|
f1.schedule
|
120
153
|
suspend
|
121
|
-
|
122
|
-
assert_equal %i[foo restarted bar baz], values
|
154
|
+
assert_equal %i[foo restarted baz], values
|
123
155
|
end
|
124
156
|
|
125
157
|
def test_restart
|
@@ -127,13 +159,13 @@ class IdleTest < MiniTest::Test
|
|
127
159
|
Fiber.new do
|
128
160
|
values << :foo
|
129
161
|
snooze
|
130
|
-
# this part will not be reached, as
|
162
|
+
# this part will not be reached, as Gyro state is reset
|
131
163
|
values << :bar
|
132
164
|
suspend
|
133
165
|
end.schedule
|
134
166
|
|
135
167
|
Fiber.new do
|
136
|
-
Gyro.
|
168
|
+
Gyro.reset!
|
137
169
|
|
138
170
|
# control is transfer to the fiber that called Gyro.restart
|
139
171
|
values << :restarted
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class ResourcePoolTest < MiniTest::Test
|
6
|
+
def test_resource_pool_limit
|
7
|
+
resources = [+'a', +'b']
|
8
|
+
pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
|
9
|
+
|
10
|
+
assert_equal 2, pool.limit
|
11
|
+
assert_equal 0, pool.available
|
12
|
+
assert_equal 0, pool.size
|
13
|
+
|
14
|
+
results = []
|
15
|
+
4.times {
|
16
|
+
spin {
|
17
|
+
snooze
|
18
|
+
pool.acquire { |resource|
|
19
|
+
results << resource
|
20
|
+
snooze
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
2.times { snooze }
|
25
|
+
assert_equal 2, pool.limit
|
26
|
+
assert_equal 0, pool.available
|
27
|
+
assert_equal 2, pool.size
|
28
|
+
|
29
|
+
2.times { snooze }
|
30
|
+
|
31
|
+
assert_equal ['a', 'b', 'a', 'b'], results
|
32
|
+
|
33
|
+
2.times { snooze }
|
34
|
+
|
35
|
+
assert_equal 2, pool.limit
|
36
|
+
assert_equal 2, pool.available
|
37
|
+
assert_equal 2, pool.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_discard
|
41
|
+
resources = [+'a', +'b']
|
42
|
+
pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
|
43
|
+
|
44
|
+
results = []
|
45
|
+
4.times {
|
46
|
+
spin {
|
47
|
+
snooze
|
48
|
+
pool.acquire { |resource|
|
49
|
+
results << resource
|
50
|
+
resource.__discard__ if resource == 'b'
|
51
|
+
snooze
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
6.times { snooze }
|
56
|
+
|
57
|
+
assert_equal ['a', 'b', 'a', 'a'], results
|
58
|
+
assert_equal 1, pool.size
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_add
|
62
|
+
resources = [+'a', +'b']
|
63
|
+
pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
|
64
|
+
|
65
|
+
pool << +'c'
|
66
|
+
|
67
|
+
results = []
|
68
|
+
4.times {
|
69
|
+
spin {
|
70
|
+
snooze
|
71
|
+
pool.acquire { |resource|
|
72
|
+
results << resource
|
73
|
+
resource.__discard__ if resource == 'b'
|
74
|
+
snooze
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
6.times { snooze }
|
79
|
+
|
80
|
+
assert_equal ['c', 'a', 'c', 'a'], results
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_single_resource_limit
|
84
|
+
resources = [+'a', +'b']
|
85
|
+
pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
|
86
|
+
|
87
|
+
results = []
|
88
|
+
10.times {
|
89
|
+
spin {
|
90
|
+
snooze
|
91
|
+
pool.acquire { |resource|
|
92
|
+
results << resource
|
93
|
+
snooze
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
20.times { snooze }
|
98
|
+
|
99
|
+
assert_equal ['a'] * 10, results
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_failing_allocator
|
103
|
+
pool = Polyphony::ResourcePool.new(limit: 4) { raise }
|
104
|
+
|
105
|
+
assert_raises { pool.acquire { } }
|
106
|
+
end
|
107
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.22'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: modulation
|
@@ -272,6 +272,7 @@ files:
|
|
272
272
|
- examples/http/config.ru
|
273
273
|
- examples/http/cuba.ru
|
274
274
|
- examples/http/happy_eyeballs.rb
|
275
|
+
- examples/http/http2_raw.rb
|
275
276
|
- examples/http/http_client.rb
|
276
277
|
- examples/http/http_get.rb
|
277
278
|
- examples/http/http_parse_experiment.rb
|
@@ -369,7 +370,11 @@ files:
|
|
369
370
|
- lib/polyphony/extensions/socket.rb
|
370
371
|
- lib/polyphony/fs.rb
|
371
372
|
- lib/polyphony/http.rb
|
372
|
-
- lib/polyphony/http/agent.rb
|
373
|
+
- lib/polyphony/http/client/agent.rb
|
374
|
+
- lib/polyphony/http/client/http1.rb
|
375
|
+
- lib/polyphony/http/client/http2.rb
|
376
|
+
- lib/polyphony/http/client/response.rb
|
377
|
+
- lib/polyphony/http/client/site_connection_manager.rb
|
373
378
|
- lib/polyphony/http/server.rb
|
374
379
|
- lib/polyphony/http/server/http1.rb
|
375
380
|
- lib/polyphony/http/server/http2.rb
|
@@ -393,6 +398,7 @@ files:
|
|
393
398
|
- test/test_http_server.rb
|
394
399
|
- test/test_io.rb
|
395
400
|
- test/test_kernel.rb
|
401
|
+
- test/test_resource_pool.rb
|
396
402
|
- test/test_signal.rb
|
397
403
|
- test/test_timer.rb
|
398
404
|
homepage: http://github.com/digital-fabric/polyphony
|
@@ -412,7 +418,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
412
418
|
requirements:
|
413
419
|
- - ">="
|
414
420
|
- !ruby/object:Gem::Version
|
415
|
-
version: '
|
421
|
+
version: '2.6'
|
416
422
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
417
423
|
requirements:
|
418
424
|
- - ">="
|