polyphony 0.73.1 → 0.77
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 +13 -10
- data/.github/workflows/test_io_uring.yml +32 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +10 -7
- data/bin/pdbg +0 -0
- data/bin/polyphony-debug +0 -0
- data/bin/stress.rb +0 -0
- data/bin/test +0 -0
- data/examples/core/trap1.rb +21 -0
- data/examples/core/trap2.rb +14 -0
- data/ext/polyphony/backend_common.c +84 -12
- data/ext/polyphony/backend_common.h +8 -0
- data/ext/polyphony/backend_io_uring.c +231 -107
- data/ext/polyphony/backend_io_uring_context.c +1 -0
- data/ext/polyphony/backend_io_uring_context.h +2 -1
- data/ext/polyphony/backend_libev.c +12 -9
- data/ext/polyphony/event.c +5 -2
- data/ext/polyphony/polyphony.c +11 -1
- data/ext/polyphony/polyphony.h +4 -1
- data/ext/polyphony/queue.c +10 -5
- data/ext/polyphony/runqueue_ring_buffer.c +3 -1
- data/ext/polyphony/socket_extensions.c +5 -2
- data/ext/test_eintr.c +50 -0
- data/lib/polyphony/extensions/fiber.rb +85 -5
- data/lib/polyphony/extensions/openssl.rb +5 -1
- data/lib/polyphony/extensions/socket.rb +12 -6
- data/lib/polyphony/extensions/thread.rb +9 -3
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +4 -1
- data/test/helper.rb +2 -6
- data/test/stress.rb +1 -1
- data/test/test_backend.rb +3 -5
- data/test/test_fiber.rb +6 -4
- data/test/test_global_api.rb +10 -14
- data/test/test_io.rb +2 -2
- data/test/test_kernel.rb +2 -2
- data/test/test_signal.rb +57 -0
- data/test/test_socket.rb +35 -2
- data/test/test_thread.rb +1 -1
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +3 -3
- data/test/test_timer.rb +2 -2
- data/test/test_trace.rb +7 -1
- metadata +11 -7
data/test/test_backend.rb
CHANGED
@@ -243,16 +243,14 @@ class BackendTest < MiniTest::Test
|
|
243
243
|
end
|
244
244
|
|
245
245
|
def test_timer_loop
|
246
|
-
|
247
|
-
|
248
|
-
i = 0
|
246
|
+
counter = 0
|
249
247
|
f = spin do
|
250
|
-
@backend.timer_loop(0.01) {
|
248
|
+
@backend.timer_loop(0.01) { counter += 1 }
|
251
249
|
end
|
252
250
|
@backend.sleep(0.05)
|
253
251
|
f.stop
|
254
252
|
f.await # TODO: check why this test sometimes segfaults if we don't a<wait fiber
|
255
|
-
assert_in_range 4..6,
|
253
|
+
assert_in_range 4..6, counter if IS_LINUX
|
256
254
|
end
|
257
255
|
|
258
256
|
class MyTimeoutException < Exception
|
data/test/test_fiber.rb
CHANGED
@@ -923,18 +923,20 @@ class MailboxTest < MiniTest::Test
|
|
923
923
|
def test_cross_thread_send_receive
|
924
924
|
ping_receive_buffer = []
|
925
925
|
pong_receive_buffer = []
|
926
|
+
master = Fiber.current
|
926
927
|
|
927
928
|
pong = Thread.new do
|
928
|
-
|
929
|
-
|
929
|
+
master << :pong_ready
|
930
|
+
3.times do
|
930
931
|
peer, data = receive
|
931
932
|
pong_receive_buffer << data
|
932
933
|
peer << 'pong'
|
933
934
|
end
|
934
935
|
end
|
935
936
|
|
937
|
+
assert_equal :pong_ready, receive
|
938
|
+
|
936
939
|
ping = Thread.new do
|
937
|
-
sleep 0.05
|
938
940
|
3.times do
|
939
941
|
pong << [Fiber.current, 'ping']
|
940
942
|
data = receive
|
@@ -943,7 +945,7 @@ class MailboxTest < MiniTest::Test
|
|
943
945
|
end
|
944
946
|
|
945
947
|
ping.join
|
946
|
-
pong.
|
948
|
+
pong.join
|
947
949
|
ping = pong = nil
|
948
950
|
|
949
951
|
assert_equal %w{pong pong pong}, ping_receive_buffer
|
data/test/test_global_api.rb
CHANGED
@@ -162,7 +162,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
162
162
|
end
|
163
163
|
t1 = Time.now
|
164
164
|
assert_equal 1, o
|
165
|
-
assert_in_range 0.008..0.015, t1 - t0
|
165
|
+
assert_in_range 0.008..0.015, t1 - t0 if IS_LINUX
|
166
166
|
|
167
167
|
t0 = Time.now
|
168
168
|
o = move_on_after(0.05, with_value: 1) do
|
@@ -172,7 +172,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
172
172
|
end
|
173
173
|
t1 = Time.now
|
174
174
|
assert_equal 2, o
|
175
|
-
assert_in_range 0.008..0.013, t1 - t0
|
175
|
+
assert_in_range 0.008..0.013, t1 - t0 if IS_LINUX
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
@@ -297,7 +297,7 @@ class SpinLoopTest < MiniTest::Test
|
|
297
297
|
f = spin_loop(rate: 100) { buffer << (counter += 1) }
|
298
298
|
sleep 0.02
|
299
299
|
f.stop
|
300
|
-
assert_in_range 1..3, counter
|
300
|
+
assert_in_range 1..3, counter if IS_LINUX
|
301
301
|
end
|
302
302
|
|
303
303
|
def test_spin_loop_with_interval
|
@@ -307,7 +307,7 @@ class SpinLoopTest < MiniTest::Test
|
|
307
307
|
f = spin_loop(interval: 0.01) { buffer << (counter += 1) }
|
308
308
|
sleep 0.02
|
309
309
|
f.stop
|
310
|
-
assert_in_range 1..3, counter
|
310
|
+
assert_in_range 1..3, counter if IS_LINUX
|
311
311
|
end
|
312
312
|
|
313
313
|
def test_spin_loop_break
|
@@ -386,10 +386,10 @@ class ThrottledLoopTest < MiniTest::Test
|
|
386
386
|
counter = 0
|
387
387
|
t0 = Time.now
|
388
388
|
f = spin do
|
389
|
-
throttled_loop(
|
389
|
+
throttled_loop(10) { buffer << (counter += 1) }
|
390
390
|
end
|
391
|
-
sleep 0.
|
392
|
-
assert_in_range 2..4, counter
|
391
|
+
sleep 0.3
|
392
|
+
assert_in_range 2..4, counter if IS_LINUX
|
393
393
|
end
|
394
394
|
|
395
395
|
def test_throttled_loop_with_count
|
@@ -417,8 +417,6 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
417
417
|
end
|
418
418
|
|
419
419
|
def test_every
|
420
|
-
skip unless IS_LINUX
|
421
|
-
|
422
420
|
buffer = []
|
423
421
|
t0 = Time.now
|
424
422
|
f = spin do
|
@@ -426,12 +424,10 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
426
424
|
end
|
427
425
|
sleep 0.05
|
428
426
|
f.stop
|
429
|
-
assert_in_range 4..6, buffer.size
|
427
|
+
assert_in_range 4..6, buffer.size if IS_LINUX
|
430
428
|
end
|
431
429
|
|
432
430
|
def test_every_with_slow_op
|
433
|
-
skip unless IS_LINUX
|
434
|
-
|
435
431
|
buffer = []
|
436
432
|
t0 = Time.now
|
437
433
|
f = spin do
|
@@ -439,14 +435,14 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
439
435
|
end
|
440
436
|
sleep 0.15
|
441
437
|
f.stop
|
442
|
-
assert_in_range 2..3, buffer.size
|
438
|
+
assert_in_range 2..3, buffer.size if IS_LINUX
|
443
439
|
end
|
444
440
|
|
445
441
|
def test_sleep
|
446
442
|
t0 = Time.now
|
447
443
|
sleep 0.1
|
448
444
|
elapsed = Time.now - t0
|
449
|
-
|
445
|
+
assert_in_range 0.05..0.15, elapsed if IS_LINUX
|
450
446
|
|
451
447
|
f = spin { sleep }
|
452
448
|
snooze
|
data/test/test_io.rb
CHANGED
@@ -354,9 +354,9 @@ class IOClassMethodsTest < MiniTest::Test
|
|
354
354
|
skip unless IS_LINUX
|
355
355
|
|
356
356
|
counter = 0
|
357
|
-
timer = spin { throttled_loop(
|
357
|
+
timer = spin { throttled_loop(20) { counter += 1 } }
|
358
358
|
|
359
|
-
IO.popen('sleep 0.
|
359
|
+
IO.popen('sleep 0.5') { |io| io.read(8192) }
|
360
360
|
assert(counter >= 5)
|
361
361
|
|
362
362
|
result = nil
|
data/test/test_kernel.rb
CHANGED
@@ -51,8 +51,8 @@ class KernelTest < MiniTest::Test
|
|
51
51
|
counter = 0
|
52
52
|
timer = spin { throttled_loop(200) { counter += 1 } }
|
53
53
|
|
54
|
-
`sleep 0.
|
55
|
-
|
54
|
+
`sleep 0.05`
|
55
|
+
assert_in_range 8..14, counter if IS_LINUX
|
56
56
|
|
57
57
|
result = `echo "hello"`
|
58
58
|
assert_equal "hello\n", result
|
data/test/test_signal.rb
CHANGED
@@ -3,6 +3,63 @@
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
5
|
class SignalTrapTest < Minitest::Test
|
6
|
+
def test_signal_handler_trace
|
7
|
+
i1, o1 = IO.pipe
|
8
|
+
i2, o2 = IO.pipe
|
9
|
+
pid = Process.pid
|
10
|
+
child_pid = Polyphony.fork do
|
11
|
+
i1.gets
|
12
|
+
Process.kill('SIGINT', pid)
|
13
|
+
sleep 0.1
|
14
|
+
o2.puts "done"
|
15
|
+
o2.close
|
16
|
+
end
|
17
|
+
|
18
|
+
events = []
|
19
|
+
begin
|
20
|
+
Thread.backend.trace_proc = proc { |*e| events << [e[0], e[1].tag] }
|
21
|
+
trap ('SIGINT') { }
|
22
|
+
|
23
|
+
o1.orig_write("\n")
|
24
|
+
o1.close
|
25
|
+
|
26
|
+
msg = i2.gets
|
27
|
+
assert_equal "done\n", msg
|
28
|
+
ensure
|
29
|
+
Thread.backend.trace_proc = nil
|
30
|
+
trap ('SIGINT') { raise Interrupt }
|
31
|
+
end
|
32
|
+
|
33
|
+
Fiber.current.tag = :main
|
34
|
+
|
35
|
+
expected = [
|
36
|
+
[:fiber_switchpoint, :main],
|
37
|
+
[:fiber_event_poll_enter, :main],
|
38
|
+
[:fiber_create, :oob],
|
39
|
+
[:fiber_schedule, :oob],
|
40
|
+
[:fiber_event_poll_leave, :main],
|
41
|
+
[:fiber_run, :oob],
|
42
|
+
[:fiber_terminate, :oob],
|
43
|
+
[:fiber_switchpoint, :oob],
|
44
|
+
[:fiber_event_poll_enter, :oob],
|
45
|
+
[:fiber_schedule, :main],
|
46
|
+
[:fiber_event_poll_leave, :oob],
|
47
|
+
[:fiber_run, :main]
|
48
|
+
]
|
49
|
+
if Thread.backend.kind == :libev
|
50
|
+
expected += [
|
51
|
+
[:fiber_schedule, :main],
|
52
|
+
[:fiber_switchpoint, :main],
|
53
|
+
[:fiber_run, :main]
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
assert_equal expected, events
|
58
|
+
ensure
|
59
|
+
Process.kill('SIGTERM', child_pid) rescue nil
|
60
|
+
Process.wait(child_pid) rescue nil
|
61
|
+
end
|
62
|
+
|
6
63
|
def test_int_signal
|
7
64
|
Thread.new { sleep 0.001; Process.kill('INT', Process.pid) }
|
8
65
|
assert_raises(Interrupt) { sleep 5 }
|
data/test/test_socket.rb
CHANGED
@@ -9,9 +9,9 @@ class SocketTest < MiniTest::Test
|
|
9
9
|
super
|
10
10
|
end
|
11
11
|
|
12
|
-
def start_tcp_server_on_random_port
|
12
|
+
def start_tcp_server_on_random_port(host = '127.0.0.1')
|
13
13
|
port = rand(1100..60000)
|
14
|
-
server = TCPServer.new(
|
14
|
+
server = TCPServer.new(host, port)
|
15
15
|
[port, server]
|
16
16
|
rescue Errno::EADDRINUSE
|
17
17
|
retry
|
@@ -40,6 +40,39 @@ class SocketTest < MiniTest::Test
|
|
40
40
|
server&.close
|
41
41
|
end
|
42
42
|
|
43
|
+
def test_tcpsocket_open_with_hostname
|
44
|
+
client = TCPSocket.open('google.com', 80)
|
45
|
+
client.write("GET / HTTP/1.0\r\nHost: google.com\r\n\r\n")
|
46
|
+
result = nil
|
47
|
+
move_on_after(1) {
|
48
|
+
result = client.read
|
49
|
+
}
|
50
|
+
assert result =~ /HTTP\/1.0 301 Moved Permanently/
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_tcp_ipv6
|
54
|
+
port, server = start_tcp_server_on_random_port('::1')
|
55
|
+
server_fiber = spin do
|
56
|
+
while (socket = server.accept)
|
57
|
+
spin do
|
58
|
+
while (data = socket.gets(8192))
|
59
|
+
socket << data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
snooze
|
66
|
+
client = TCPSocket.new('::1', port)
|
67
|
+
client.write("1234\n")
|
68
|
+
assert_equal "1234\n", client.recv(8192)
|
69
|
+
client.close
|
70
|
+
ensure
|
71
|
+
server_fiber&.stop
|
72
|
+
server_fiber&.await
|
73
|
+
server&.close
|
74
|
+
end
|
75
|
+
|
43
76
|
def test_read
|
44
77
|
port, server = start_tcp_server_on_random_port
|
45
78
|
server_fiber = spin do
|
data/test/test_thread.rb
CHANGED
@@ -132,7 +132,7 @@ class ThreadTest < MiniTest::Test
|
|
132
132
|
Thread.backend.trace_proc = proc {|*r| records << r }
|
133
133
|
suspend
|
134
134
|
assert_equal [
|
135
|
-
[:fiber_switchpoint, Fiber.current]
|
135
|
+
[:fiber_switchpoint, Fiber.current, ["#{__FILE__}:#{__LINE__ - 2}:in `test_that_suspend_returns_immediately_if_no_watchers'"] + caller]
|
136
136
|
], records
|
137
137
|
ensure
|
138
138
|
Thread.backend.trace_proc = nil
|
data/test/test_thread_pool.rb
CHANGED
data/test/test_throttler.rb
CHANGED
@@ -10,7 +10,7 @@ class ThrottlerTest < MiniTest::Test
|
|
10
10
|
f = spin { loop { t.process { buffer << 1 } } }
|
11
11
|
sleep 0.2
|
12
12
|
f.stop
|
13
|
-
assert_in_range 1..4, buffer.size
|
13
|
+
assert_in_range 1..4, buffer.size if IS_LINUX
|
14
14
|
ensure
|
15
15
|
t.stop
|
16
16
|
end
|
@@ -23,7 +23,7 @@ class ThrottlerTest < MiniTest::Test
|
|
23
23
|
end
|
24
24
|
sleep 0.25
|
25
25
|
f.stop
|
26
|
-
assert_in_range
|
26
|
+
assert_in_range 4..6, buffer.size if IS_LINUX
|
27
27
|
ensure
|
28
28
|
t.stop
|
29
29
|
end
|
@@ -34,7 +34,7 @@ class ThrottlerTest < MiniTest::Test
|
|
34
34
|
f = spin { loop { t.process { buffer << 1 } } }
|
35
35
|
sleep 0.02
|
36
36
|
f.stop
|
37
|
-
assert_in_range 2..4, buffer.size
|
37
|
+
assert_in_range 2..4, buffer.size if IS_LINUX
|
38
38
|
ensure
|
39
39
|
t.stop
|
40
40
|
end
|
data/test/test_timer.rb
CHANGED
@@ -78,7 +78,7 @@ class TimerCancelAfterTest < MiniTest::Test
|
|
78
78
|
|
79
79
|
def test_timer_cancel_after_with_reset
|
80
80
|
buf = []
|
81
|
-
@timer.cancel_after(0.
|
81
|
+
@timer.cancel_after(0.15) do
|
82
82
|
sleep 0.05
|
83
83
|
buf << 1
|
84
84
|
@timer.reset
|
@@ -160,6 +160,6 @@ class TimerMiscTest < MiniTest::Test
|
|
160
160
|
end
|
161
161
|
sleep 0.05
|
162
162
|
f.stop
|
163
|
-
assert_in_range 3..7, buffer.size
|
163
|
+
assert_in_range 3..7, buffer.size if IS_LINUX
|
164
164
|
end
|
165
165
|
end
|
data/test/test_trace.rb
CHANGED
@@ -10,7 +10,7 @@ class TraceTest < MiniTest::Test
|
|
10
10
|
|
11
11
|
assert_equal [
|
12
12
|
[:fiber_schedule, Fiber.current, nil, false],
|
13
|
-
[:fiber_switchpoint, Fiber.current],
|
13
|
+
[:fiber_switchpoint, Fiber.current, ["#{__FILE__}:#{__LINE__ - 4}:in `test_tracing_enabled'"] + caller],
|
14
14
|
[:fiber_run, Fiber.current, nil]
|
15
15
|
], events
|
16
16
|
ensure
|
@@ -22,9 +22,15 @@ class TraceTest < MiniTest::Test
|
|
22
22
|
Thread.backend.trace_proc = proc { |*e| events << e }
|
23
23
|
|
24
24
|
f = spin { sleep 0; :byebye }
|
25
|
+
l0 = __LINE__ + 1
|
25
26
|
suspend
|
26
27
|
sleep 0
|
27
28
|
|
29
|
+
Thread.backend.trace_proc = nil
|
30
|
+
|
31
|
+
# remove caller info for :fiber_switchpoint events
|
32
|
+
events.each {|e| e.pop if e[0] == :fiber_switchpoint }
|
33
|
+
|
28
34
|
assert_equal [
|
29
35
|
[:fiber_create, f],
|
30
36
|
[:fiber_schedule, f, nil, false],
|
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.77'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -136,7 +136,7 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 1.1.4
|
139
|
-
description:
|
139
|
+
description:
|
140
140
|
email: sharon@noteflakes.com
|
141
141
|
executables: []
|
142
142
|
extensions:
|
@@ -146,6 +146,7 @@ extra_rdoc_files:
|
|
146
146
|
files:
|
147
147
|
- ".github/FUNDING.yml"
|
148
148
|
- ".github/workflows/test.yml"
|
149
|
+
- ".github/workflows/test_io_uring.yml"
|
149
150
|
- ".gitignore"
|
150
151
|
- ".gitmodules"
|
151
152
|
- ".rubocop.yml"
|
@@ -244,6 +245,8 @@ files:
|
|
244
245
|
- examples/core/thread_pool.rb
|
245
246
|
- examples/core/throttling.rb
|
246
247
|
- examples/core/timeout.rb
|
248
|
+
- examples/core/trap1.rb
|
249
|
+
- examples/core/trap2.rb
|
247
250
|
- examples/core/using-a-mutex.rb
|
248
251
|
- examples/core/worker-thread.rb
|
249
252
|
- examples/io/backticks.rb
|
@@ -345,6 +348,7 @@ files:
|
|
345
348
|
- ext/polyphony/runqueue_ring_buffer.h
|
346
349
|
- ext/polyphony/socket_extensions.c
|
347
350
|
- ext/polyphony/thread.c
|
351
|
+
- ext/test_eintr.c
|
348
352
|
- lib/polyphony.rb
|
349
353
|
- lib/polyphony/adapters/fs.rb
|
350
354
|
- lib/polyphony/adapters/irb.rb
|
@@ -411,7 +415,7 @@ metadata:
|
|
411
415
|
documentation_uri: https://digital-fabric.github.io/polyphony/
|
412
416
|
homepage_uri: https://digital-fabric.github.io/polyphony/
|
413
417
|
changelog_uri: https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md
|
414
|
-
post_install_message:
|
418
|
+
post_install_message:
|
415
419
|
rdoc_options:
|
416
420
|
- "--title"
|
417
421
|
- polyphony
|
@@ -430,8 +434,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
430
434
|
- !ruby/object:Gem::Version
|
431
435
|
version: '0'
|
432
436
|
requirements: []
|
433
|
-
rubygems_version: 3.
|
434
|
-
signing_key:
|
437
|
+
rubygems_version: 3.3.3
|
438
|
+
signing_key:
|
435
439
|
specification_version: 4
|
436
440
|
summary: Fine grained concurrency for Ruby
|
437
441
|
test_files: []
|