polyphony 0.21 → 0.22
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|