polyphony 0.51.0 → 0.54.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/.github/workflows/test.yml +2 -2
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +7 -68
- data/TODO.md +37 -6
- data/examples/core/forking.rb +2 -2
- data/examples/core/queue.rb +19 -0
- data/examples/io/echo_server.rb +1 -0
- data/examples/io/https_server.rb +30 -0
- data/examples/io/tcp_proxy.rb +2 -2
- data/ext/polyphony/backend_common.h +29 -6
- data/ext/polyphony/backend_io_uring.c +125 -23
- data/ext/polyphony/backend_io_uring_context.c +1 -0
- data/ext/polyphony/backend_io_uring_context.h +1 -0
- data/ext/polyphony/backend_libev.c +309 -21
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +9 -2
- data/ext/polyphony/polyphony.c +102 -0
- data/ext/polyphony/polyphony.h +32 -2
- data/ext/polyphony/polyphony_ext.c +3 -0
- data/ext/polyphony/queue.c +1 -1
- data/ext/polyphony/runqueue.c +1 -1
- data/ext/polyphony/socket_extensions.c +33 -0
- data/ext/polyphony/thread.c +8 -2
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/core/global_api.rb +5 -5
- data/lib/polyphony/core/sync.rb +9 -1
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/extensions/core.rb +1 -1
- data/lib/polyphony/extensions/io.rb +20 -25
- data/lib/polyphony/extensions/openssl.rb +28 -21
- data/lib/polyphony/extensions/socket.rb +51 -54
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -5
- data/test/helper.rb +1 -1
- data/test/stress.rb +2 -0
- data/test/test_backend.rb +152 -5
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +33 -2
- data/test/test_kernel.rb +1 -1
- data/test/test_signal.rb +1 -1
- data/test/test_socket.rb +27 -0
- data/test/test_sync.rb +43 -0
- data/test/test_thread.rb +4 -0
- data/test/test_timer.rb +1 -1
- metadata +10 -49
data/test/test_backend.rb
CHANGED
@@ -125,12 +125,15 @@ class BackendTest < MiniTest::Test
|
|
125
125
|
assert_equal [:ready, 'foo', 'bar'], buf
|
126
126
|
end
|
127
127
|
|
128
|
-
|
129
|
-
|
128
|
+
Net = Polyphony::Net
|
129
|
+
|
130
|
+
def test_accept
|
131
|
+
server = Net.listening_socket_from_options('127.0.0.1', 1234, reuse_addr: true)
|
130
132
|
|
131
133
|
clients = []
|
132
|
-
server_fiber =
|
133
|
-
@backend.
|
134
|
+
server_fiber = spin_loop do
|
135
|
+
c = @backend.accept(server, TCPSocket)
|
136
|
+
clients << c
|
134
137
|
end
|
135
138
|
|
136
139
|
c1 = TCPSocket.new('127.0.0.1', 1234)
|
@@ -146,7 +149,32 @@ class BackendTest < MiniTest::Test
|
|
146
149
|
ensure
|
147
150
|
c1&.close
|
148
151
|
c2&.close
|
149
|
-
server_fiber
|
152
|
+
server_fiber&.stop
|
153
|
+
snooze
|
154
|
+
server&.close
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_accept_loop
|
158
|
+
server = Net.listening_socket_from_options('127.0.0.1', 1235, reuse_addr: true)
|
159
|
+
|
160
|
+
clients = []
|
161
|
+
server_fiber = spin do
|
162
|
+
@backend.accept_loop(server, TCPSocket) { |c| clients << c }
|
163
|
+
end
|
164
|
+
|
165
|
+
c1 = TCPSocket.new('127.0.0.1', 1235)
|
166
|
+
sleep 0.01
|
167
|
+
|
168
|
+
assert_equal 1, clients.size
|
169
|
+
|
170
|
+
c2 = TCPSocket.new('127.0.0.1', 1235)
|
171
|
+
sleep 0.01
|
172
|
+
|
173
|
+
assert_equal 2, clients.size
|
174
|
+
ensure
|
175
|
+
c1&.close
|
176
|
+
c2&.close
|
177
|
+
server_fiber&.stop
|
150
178
|
snooze
|
151
179
|
server&.close
|
152
180
|
end
|
@@ -209,4 +237,123 @@ class BackendTest < MiniTest::Test
|
|
209
237
|
end
|
210
238
|
assert_equal [1], buffer
|
211
239
|
end
|
240
|
+
|
241
|
+
def test_splice
|
242
|
+
i1, o1 = IO.pipe
|
243
|
+
i2, o2 = IO.pipe
|
244
|
+
len = nil
|
245
|
+
|
246
|
+
spin {
|
247
|
+
len = o2.splice(i1, 1000)
|
248
|
+
o2.close
|
249
|
+
}
|
250
|
+
|
251
|
+
o1.write('foobar')
|
252
|
+
result = i2.read
|
253
|
+
|
254
|
+
assert_equal 'foobar', result
|
255
|
+
assert_equal 6, len
|
256
|
+
end
|
257
|
+
|
258
|
+
def test_splice_to_eof
|
259
|
+
i1, o1 = IO.pipe
|
260
|
+
i2, o2 = IO.pipe
|
261
|
+
len = nil
|
262
|
+
|
263
|
+
f = spin {
|
264
|
+
len = o2.splice_to_eof(i1, 1000)
|
265
|
+
o2.close
|
266
|
+
}
|
267
|
+
|
268
|
+
o1.write('foo')
|
269
|
+
result = i2.readpartial(1000)
|
270
|
+
assert_equal 'foo', result
|
271
|
+
|
272
|
+
o1.write('bar')
|
273
|
+
result = i2.readpartial(1000)
|
274
|
+
assert_equal 'bar', result
|
275
|
+
o1.close
|
276
|
+
f.await
|
277
|
+
assert_equal 6, len
|
278
|
+
ensure
|
279
|
+
if f.alive?
|
280
|
+
f.interrupt
|
281
|
+
f.await
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
class BackendChainTest < MiniTest::Test
|
287
|
+
def setup
|
288
|
+
super
|
289
|
+
@prev_backend = Thread.current.backend
|
290
|
+
@backend = Polyphony::Backend.new
|
291
|
+
Thread.current.backend = @backend
|
292
|
+
end
|
293
|
+
|
294
|
+
def teardown
|
295
|
+
@backend.finalize
|
296
|
+
Thread.current.backend = @prev_backend
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_simple_write_chain
|
300
|
+
i, o = IO.pipe
|
301
|
+
|
302
|
+
result = Thread.backend.chain(
|
303
|
+
[:write, o, 'hello'],
|
304
|
+
[:write, o, ' world']
|
305
|
+
)
|
306
|
+
|
307
|
+
assert_equal 6, result
|
308
|
+
o.close
|
309
|
+
assert_equal 'hello world', i.read
|
310
|
+
end
|
311
|
+
|
312
|
+
def chunk_header(len)
|
313
|
+
"Content-Length: #{len}\r\n\r\n"
|
314
|
+
end
|
315
|
+
|
316
|
+
def serve_io(from, to)
|
317
|
+
i, o = IO.pipe
|
318
|
+
backend = Thread.current.backend
|
319
|
+
while true
|
320
|
+
len = o.splice(from, 8192)
|
321
|
+
break if len == 0
|
322
|
+
|
323
|
+
backend.chain(
|
324
|
+
[:write, to, chunk_header(len)],
|
325
|
+
[:splice, i, to, len]
|
326
|
+
)
|
327
|
+
end
|
328
|
+
to.close
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_chain_with_splice
|
332
|
+
from_r, from_w = IO.pipe
|
333
|
+
to_r, to_w = IO.pipe
|
334
|
+
|
335
|
+
result = nil
|
336
|
+
f = spin { serve_io(from_r, to_w) }
|
337
|
+
|
338
|
+
from_w << 'Hello world!'
|
339
|
+
from_w.close
|
340
|
+
|
341
|
+
assert_equal "Content-Length: 12\r\n\r\nHello world!", to_r.read
|
342
|
+
end
|
343
|
+
|
344
|
+
def test_invalid_op
|
345
|
+
i, o = IO.pipe
|
346
|
+
|
347
|
+
assert_raises(RuntimeError) {
|
348
|
+
Thread.backend.chain(
|
349
|
+
[:read, o]
|
350
|
+
)
|
351
|
+
}
|
352
|
+
|
353
|
+
assert_raises(RuntimeError) {
|
354
|
+
Thread.backend.chain(
|
355
|
+
[:write, o]
|
356
|
+
)
|
357
|
+
}
|
358
|
+
end
|
212
359
|
end
|
data/test/test_global_api.rb
CHANGED
@@ -160,10 +160,10 @@ class MoveOnAfterTest < MiniTest::Test
|
|
160
160
|
end
|
161
161
|
t1 = Time.now
|
162
162
|
assert_equal 1, o
|
163
|
-
assert_in_range 0.008..0.
|
163
|
+
assert_in_range 0.008..0.015, t1 - t0
|
164
164
|
|
165
165
|
t0 = Time.now
|
166
|
-
o = move_on_after(0.
|
166
|
+
o = move_on_after(0.05, with_value: 1) do
|
167
167
|
move_on_after(0.01, with_value: 2) do
|
168
168
|
sleep 1
|
169
169
|
end
|
data/test/test_io.rb
CHANGED
@@ -195,7 +195,7 @@ class IOTest < MiniTest::Test
|
|
195
195
|
assert_equal ['foo', 'bar', 'baz'], buffer
|
196
196
|
end
|
197
197
|
|
198
|
-
class
|
198
|
+
class Receiver1
|
199
199
|
attr_reader :buffer
|
200
200
|
|
201
201
|
def initialize
|
@@ -209,7 +209,7 @@ class IOTest < MiniTest::Test
|
|
209
209
|
|
210
210
|
def test_feed_loop_without_block
|
211
211
|
i, o = IO.pipe
|
212
|
-
receiver =
|
212
|
+
receiver = Receiver1.new
|
213
213
|
reader = spin do
|
214
214
|
i.feed_loop(receiver, :recv)
|
215
215
|
end
|
@@ -225,6 +225,37 @@ class IOTest < MiniTest::Test
|
|
225
225
|
sleep 0.01
|
226
226
|
assert_equal ['foo', 'bar', 'baz'], receiver.buffer
|
227
227
|
end
|
228
|
+
|
229
|
+
class Receiver2
|
230
|
+
attr_reader :buffer
|
231
|
+
|
232
|
+
def initialize
|
233
|
+
@buffer = []
|
234
|
+
end
|
235
|
+
|
236
|
+
def call(obj)
|
237
|
+
@buffer << obj
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_feed_loop_without_method
|
242
|
+
i, o = IO.pipe
|
243
|
+
receiver = Receiver2.new
|
244
|
+
reader = spin do
|
245
|
+
i.feed_loop(receiver)
|
246
|
+
end
|
247
|
+
o << 'foo'
|
248
|
+
sleep 0.01
|
249
|
+
assert_equal ['foo'], receiver.buffer
|
250
|
+
|
251
|
+
o << 'bar'
|
252
|
+
sleep 0.01
|
253
|
+
assert_equal ['foo', 'bar'], receiver.buffer
|
254
|
+
|
255
|
+
o << 'baz'
|
256
|
+
sleep 0.01
|
257
|
+
assert_equal ['foo', 'bar', 'baz'], receiver.buffer
|
258
|
+
end
|
228
259
|
end
|
229
260
|
|
230
261
|
class IOClassMethodsTest < MiniTest::Test
|
data/test/test_kernel.rb
CHANGED
@@ -21,7 +21,7 @@ class KernelTest < MiniTest::Test
|
|
21
21
|
|
22
22
|
def test_Kernel_system_singleton_method
|
23
23
|
assert_equal true, Kernel.system("which ruby > /dev/null 2>&1")
|
24
|
-
assert_equal false, Kernel.system("
|
24
|
+
assert_equal false, Kernel.system("azertyuiop > /dev/null 2>&1")
|
25
25
|
end
|
26
26
|
|
27
27
|
def patch_open3
|
data/test/test_signal.rb
CHANGED
data/test/test_socket.rb
CHANGED
@@ -34,6 +34,33 @@ class SocketTest < MiniTest::Test
|
|
34
34
|
server&.close
|
35
35
|
end
|
36
36
|
|
37
|
+
# sending multiple strings at once
|
38
|
+
def test_sendv
|
39
|
+
port = rand(1234..5678)
|
40
|
+
server = TCPServer.new('127.0.0.1', port)
|
41
|
+
|
42
|
+
server_fiber = spin do
|
43
|
+
while (socket = server.accept)
|
44
|
+
spin do
|
45
|
+
while (data = socket.gets(8192))
|
46
|
+
socket.write("you said ", data)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
snooze
|
53
|
+
client = TCPSocket.new('127.0.0.1', port)
|
54
|
+
client.write("1234\n")
|
55
|
+
assert_equal "you said 1234\n", client.recv(8192)
|
56
|
+
client.close
|
57
|
+
ensure
|
58
|
+
server_fiber&.stop
|
59
|
+
server_fiber&.await
|
60
|
+
server&.close
|
61
|
+
end
|
62
|
+
|
63
|
+
|
37
64
|
def test_feed_loop
|
38
65
|
port = rand(1234..5678)
|
39
66
|
server = TCPServer.new('127.0.0.1', port)
|
data/test/test_sync.rb
CHANGED
@@ -70,4 +70,47 @@ class MutexTest < MiniTest::Test
|
|
70
70
|
Fiber.current.await_all_children
|
71
71
|
assert_equal [:bar, :foo], buf
|
72
72
|
end
|
73
|
+
|
74
|
+
def test_owned?
|
75
|
+
buf = []
|
76
|
+
lock = Polyphony::Mutex.new
|
77
|
+
(1..3).each do |i|
|
78
|
+
spin do
|
79
|
+
lock.synchronize do
|
80
|
+
buf << ">> #{i}"
|
81
|
+
buf << [i, lock.owned?]
|
82
|
+
sleep(rand * 0.05)
|
83
|
+
buf << "<< #{i}"
|
84
|
+
end
|
85
|
+
buf << [i, lock.owned?]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Fiber.current.await_all_children
|
90
|
+
assert_equal ['>> 1', [1, true], '<< 1', [1, false], '>> 2', [2, true], '<< 2', [2, false], '>> 3', [3, true], '<< 3', [3, false]], buf
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_locked?
|
94
|
+
lock = Polyphony::Mutex.new
|
95
|
+
a = spin do
|
96
|
+
sender = receive
|
97
|
+
lock.synchronize do
|
98
|
+
sender << 'pong'
|
99
|
+
receive
|
100
|
+
end
|
101
|
+
sender << 'pong'
|
102
|
+
end
|
103
|
+
|
104
|
+
snooze
|
105
|
+
assert !lock.locked?
|
106
|
+
a << Fiber.current
|
107
|
+
|
108
|
+
receive
|
109
|
+
assert lock.locked?
|
110
|
+
|
111
|
+
a << Fiber.current
|
112
|
+
|
113
|
+
receive
|
114
|
+
assert !lock.locked?
|
115
|
+
end
|
73
116
|
end
|
data/test/test_thread.rb
CHANGED
@@ -124,6 +124,10 @@ class ThreadTest < MiniTest::Test
|
|
124
124
|
t&.join
|
125
125
|
end
|
126
126
|
|
127
|
+
def test_backend_class_method
|
128
|
+
assert_equal Thread.current.backend, Thread.backend
|
129
|
+
end
|
130
|
+
|
127
131
|
def test_that_suspend_returns_immediately_if_no_watchers
|
128
132
|
records = []
|
129
133
|
t = Polyphony::Trace.new(:fiber_all) do |r|
|
data/test/test_timer.rb
CHANGED
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.54.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.
|
33
|
+
version: 5.14.4
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.
|
40
|
+
version: 5.14.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest-reporters
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -227,61 +227,19 @@ dependencies:
|
|
227
227
|
- !ruby/object:Gem::Version
|
228
228
|
version: 0.17.1
|
229
229
|
- !ruby/object:Gem::Dependency
|
230
|
-
name:
|
230
|
+
name: localhost
|
231
231
|
requirement: !ruby/object:Gem::Requirement
|
232
232
|
requirements:
|
233
233
|
- - "~>"
|
234
234
|
- !ruby/object:Gem::Version
|
235
|
-
version:
|
236
|
-
type: :development
|
237
|
-
prerelease: false
|
238
|
-
version_requirements: !ruby/object:Gem::Requirement
|
239
|
-
requirements:
|
240
|
-
- - "~>"
|
241
|
-
- !ruby/object:Gem::Version
|
242
|
-
version: 3.8.6
|
243
|
-
- !ruby/object:Gem::Dependency
|
244
|
-
name: jekyll-remote-theme
|
245
|
-
requirement: !ruby/object:Gem::Requirement
|
246
|
-
requirements:
|
247
|
-
- - "~>"
|
248
|
-
- !ruby/object:Gem::Version
|
249
|
-
version: 0.4.1
|
250
|
-
type: :development
|
251
|
-
prerelease: false
|
252
|
-
version_requirements: !ruby/object:Gem::Requirement
|
253
|
-
requirements:
|
254
|
-
- - "~>"
|
255
|
-
- !ruby/object:Gem::Version
|
256
|
-
version: 0.4.1
|
257
|
-
- !ruby/object:Gem::Dependency
|
258
|
-
name: jekyll-seo-tag
|
259
|
-
requirement: !ruby/object:Gem::Requirement
|
260
|
-
requirements:
|
261
|
-
- - "~>"
|
262
|
-
- !ruby/object:Gem::Version
|
263
|
-
version: 2.6.1
|
264
|
-
type: :development
|
265
|
-
prerelease: false
|
266
|
-
version_requirements: !ruby/object:Gem::Requirement
|
267
|
-
requirements:
|
268
|
-
- - "~>"
|
269
|
-
- !ruby/object:Gem::Version
|
270
|
-
version: 2.6.1
|
271
|
-
- !ruby/object:Gem::Dependency
|
272
|
-
name: just-the-docs
|
273
|
-
requirement: !ruby/object:Gem::Requirement
|
274
|
-
requirements:
|
275
|
-
- - "~>"
|
276
|
-
- !ruby/object:Gem::Version
|
277
|
-
version: 0.3.0
|
235
|
+
version: 1.1.4
|
278
236
|
type: :development
|
279
237
|
prerelease: false
|
280
238
|
version_requirements: !ruby/object:Gem::Requirement
|
281
239
|
requirements:
|
282
240
|
- - "~>"
|
283
241
|
- !ruby/object:Gem::Version
|
284
|
-
version:
|
242
|
+
version: 1.1.4
|
285
243
|
description:
|
286
244
|
email: sharon@noteflakes.com
|
287
245
|
executables: []
|
@@ -369,6 +327,7 @@ files:
|
|
369
327
|
- examples/core/interrupt.rb
|
370
328
|
- examples/core/nested.rb
|
371
329
|
- examples/core/pingpong.rb
|
330
|
+
- examples/core/queue.rb
|
372
331
|
- examples/core/recurrent-timer.rb
|
373
332
|
- examples/core/resource_delegate.rb
|
374
333
|
- examples/core/spin.rb
|
@@ -392,6 +351,7 @@ files:
|
|
392
351
|
- examples/io/echo_stdin.rb
|
393
352
|
- examples/io/happy-eyeballs.rb
|
394
353
|
- examples/io/httparty.rb
|
354
|
+
- examples/io/https_server.rb
|
395
355
|
- examples/io/irb.rb
|
396
356
|
- examples/io/net-http.rb
|
397
357
|
- examples/io/open.rb
|
@@ -474,6 +434,7 @@ files:
|
|
474
434
|
- ext/polyphony/runqueue.c
|
475
435
|
- ext/polyphony/runqueue_ring_buffer.c
|
476
436
|
- ext/polyphony/runqueue_ring_buffer.h
|
437
|
+
- ext/polyphony/socket_extensions.c
|
477
438
|
- ext/polyphony/thread.c
|
478
439
|
- ext/polyphony/tracing.c
|
479
440
|
- lib/polyphony.rb
|