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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c88e804739b62a55f7647c57e55582d2ae5cf6a73bca55160aa90138b80217a
|
4
|
+
data.tar.gz: ecbeb0aeed96a7b6f21f8d41b67195574213b8b01b51bccd0f1bcf59165ab9f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93ff227cab8891c79bc4447c25359df03cd675ada579d13c5dc09d1f18e42fd00ed1b3ce5e433bd5addf83076af3a45c29cdd49ae8cb2cb9f19892cb27a40476
|
7
|
+
data.tar.gz: 1e1f39c19d2edbd49406c3a3917848951914c48b81316fff6ff78f381fb7db70b407dc5428b94a45292261e202df1c91da5d4e971ab19ba91e7c96e0bbbb91bc
|
data/.github/workflows/test.yml
CHANGED
@@ -8,7 +8,7 @@ jobs:
|
|
8
8
|
fail-fast: false
|
9
9
|
matrix:
|
10
10
|
os: [ubuntu-latest]
|
11
|
-
ruby: [2.6, 2.7]
|
11
|
+
ruby: [2.6, 2.7, 3.0]
|
12
12
|
|
13
13
|
name: >-
|
14
14
|
${{matrix.os}}, ${{matrix.ruby}}
|
@@ -26,6 +26,6 @@ jobs:
|
|
26
26
|
- name: Show Linux kernel version
|
27
27
|
run: uname -r
|
28
28
|
- name: Compile C-extension
|
29
|
-
run: bundle exec rake compile
|
29
|
+
run: POLYPHONY_USE_LIBEV=1 bundle exec rake compile
|
30
30
|
- name: Run tests
|
31
31
|
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
## 0.54.0
|
2
|
+
|
3
|
+
- Implement Mutex#owned?, #locked? (#50)
|
4
|
+
- Fix arity for SSLSocket#peeraddr (#55)
|
5
|
+
- Add missing SSLServer#accept_loop method (#53)
|
6
|
+
- Fix SSLSocket buffering behaviour
|
7
|
+
- Add recv_loop alias for SSLSocket (#54)
|
8
|
+
|
9
|
+
## 0.53.2
|
10
|
+
|
11
|
+
- Remove `splice` methods on libev backend on non-Linux OS (#43)
|
12
|
+
|
13
|
+
## 0.53.0
|
14
|
+
|
15
|
+
- Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
|
16
|
+
|
17
|
+
## 0.52.0
|
18
|
+
|
19
|
+
- Polyphony is now compatible with Ruby 3.0
|
20
|
+
- Add `Backend#sendv` method for sending multiple strings
|
21
|
+
- Accept flags argument in `Backend#send` (#48)
|
22
|
+
- Fix io_uring backend on Ruby 3.0 (#47)
|
23
|
+
- Implement C-based public backend API: `Polyphony.backend_XXXX` methods
|
24
|
+
- libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
|
25
|
+
- Use `:call` as default method in `#feed_loop`
|
26
|
+
|
1
27
|
## 0.51.0
|
2
28
|
|
3
29
|
- Implement `IO#feed_loop`, `Socket#feed_loop`
|
data/Gemfile.lock
CHANGED
@@ -1,72 +1,28 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.54.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
addressable (2.7.0)
|
10
|
-
public_suffix (>= 2.0.2, < 5.0)
|
11
9
|
ansi (1.5.0)
|
12
10
|
ast (2.4.0)
|
13
11
|
builder (3.2.4)
|
14
12
|
coderay (1.1.3)
|
15
|
-
colorator (1.1.0)
|
16
|
-
concurrent-ruby (1.1.6)
|
17
13
|
docile (1.3.2)
|
18
|
-
em-websocket (0.5.1)
|
19
|
-
eventmachine (>= 0.12.9)
|
20
|
-
http_parser.rb (~> 0.6.0)
|
21
|
-
eventmachine (1.2.7)
|
22
|
-
ffi (1.12.1)
|
23
|
-
forwardable-extended (2.6.0)
|
24
14
|
hiredis (0.6.3)
|
25
15
|
http_parser.rb (0.6.0)
|
26
16
|
httparty (0.17.1)
|
27
17
|
mime-types (~> 3.0)
|
28
18
|
multi_xml (>= 0.5.2)
|
29
|
-
i18n (0.9.5)
|
30
|
-
concurrent-ruby (~> 1.0)
|
31
|
-
jekyll (3.8.6)
|
32
|
-
addressable (~> 2.4)
|
33
|
-
colorator (~> 1.0)
|
34
|
-
em-websocket (~> 0.5)
|
35
|
-
i18n (~> 0.7)
|
36
|
-
jekyll-sass-converter (~> 1.0)
|
37
|
-
jekyll-watch (~> 2.0)
|
38
|
-
kramdown (~> 1.14)
|
39
|
-
liquid (~> 4.0)
|
40
|
-
mercenary (~> 0.3.3)
|
41
|
-
pathutil (~> 0.9)
|
42
|
-
rouge (>= 1.7, < 4)
|
43
|
-
safe_yaml (~> 1.0)
|
44
|
-
jekyll-remote-theme (0.4.1)
|
45
|
-
addressable (~> 2.0)
|
46
|
-
jekyll (>= 3.5, < 5.0)
|
47
|
-
rubyzip (>= 1.3.0)
|
48
|
-
jekyll-sass-converter (1.5.2)
|
49
|
-
sass (~> 3.4)
|
50
|
-
jekyll-seo-tag (2.6.1)
|
51
|
-
jekyll (>= 3.3, < 5.0)
|
52
|
-
jekyll-watch (2.2.1)
|
53
|
-
listen (~> 3.0)
|
54
19
|
json (2.3.0)
|
55
|
-
|
56
|
-
jekyll (>= 3.8.5)
|
57
|
-
jekyll-seo-tag (~> 2.0)
|
58
|
-
rake (>= 12.3.1, < 13.1.0)
|
59
|
-
kramdown (1.17.0)
|
60
|
-
liquid (4.0.3)
|
61
|
-
listen (3.2.1)
|
62
|
-
rb-fsevent (~> 0.10, >= 0.10.3)
|
63
|
-
rb-inotify (~> 0.9, >= 0.9.10)
|
64
|
-
mercenary (0.3.6)
|
20
|
+
localhost (1.1.8)
|
65
21
|
method_source (1.0.0)
|
66
22
|
mime-types (3.3.1)
|
67
23
|
mime-types-data (~> 3.2015)
|
68
24
|
mime-types-data (3.2020.0512)
|
69
|
-
minitest (5.
|
25
|
+
minitest (5.14.4)
|
70
26
|
minitest-reporters (1.4.2)
|
71
27
|
ansi
|
72
28
|
builder
|
@@ -78,25 +34,18 @@ GEM
|
|
78
34
|
parallel (1.19.1)
|
79
35
|
parser (2.7.0.2)
|
80
36
|
ast (~> 2.4.0)
|
81
|
-
pathutil (0.16.2)
|
82
|
-
forwardable-extended (~> 2.6)
|
83
37
|
pg (1.1.4)
|
84
38
|
pry (0.13.1)
|
85
39
|
coderay (~> 1.1)
|
86
40
|
method_source (~> 1.0)
|
87
|
-
public_suffix (4.0.3)
|
88
41
|
rack (2.2.3)
|
89
42
|
rainbow (3.0.0)
|
90
|
-
rake (
|
43
|
+
rake (13.0.3)
|
91
44
|
rake-compiler (1.1.1)
|
92
45
|
rake
|
93
|
-
rb-fsevent (0.10.3)
|
94
|
-
rb-inotify (0.10.1)
|
95
|
-
ffi (~> 1.0)
|
96
46
|
redis (4.1.0)
|
97
47
|
regexp_parser (1.7.1)
|
98
48
|
rexml (3.2.4)
|
99
|
-
rouge (3.15.0)
|
100
49
|
rubocop (0.85.1)
|
101
50
|
parallel (~> 1.10)
|
102
51
|
parser (>= 2.7.0.1)
|
@@ -109,13 +58,6 @@ GEM
|
|
109
58
|
rubocop-ast (0.0.3)
|
110
59
|
parser (>= 2.7.0.1)
|
111
60
|
ruby-progressbar (1.10.1)
|
112
|
-
rubyzip (2.0.0)
|
113
|
-
safe_yaml (1.0.5)
|
114
|
-
sass (3.7.4)
|
115
|
-
sass-listen (~> 4.0.0)
|
116
|
-
sass-listen (4.0.0)
|
117
|
-
rb-fsevent (~> 0.9, >= 0.9.4)
|
118
|
-
rb-inotify (~> 0.9, >= 0.9.7)
|
119
61
|
sequel (5.34.0)
|
120
62
|
simplecov (0.17.1)
|
121
63
|
docile (~> 1.1)
|
@@ -131,11 +73,8 @@ DEPENDENCIES
|
|
131
73
|
hiredis (= 0.6.3)
|
132
74
|
http_parser.rb (~> 0.6.0)
|
133
75
|
httparty (= 0.17.1)
|
134
|
-
|
135
|
-
|
136
|
-
jekyll-seo-tag (~> 2.6.1)
|
137
|
-
just-the-docs (~> 0.3.0)
|
138
|
-
minitest (= 5.13.0)
|
76
|
+
localhost (~> 1.1.4)
|
77
|
+
minitest (= 5.14.4)
|
139
78
|
minitest-reporters (= 1.4.2)
|
140
79
|
msgpack (= 1.4.2)
|
141
80
|
mysql2 (= 0.5.3)
|
@@ -150,4 +89,4 @@ DEPENDENCIES
|
|
150
89
|
simplecov (= 0.17.1)
|
151
90
|
|
152
91
|
BUNDLED WITH
|
153
|
-
2.
|
92
|
+
2.2.3
|
data/TODO.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
- Add support for IPv6:
|
2
|
+
https://www.reddit.com/r/ruby/comments/lyen23/understanding_ipv6_and_why_its_important_to_you/
|
3
|
+
|
4
|
+
- Add support for UDP sockets
|
5
|
+
|
1
6
|
- Check segfault when resetting a `cancel_after` timeout lots of times at very high rate
|
2
7
|
- Check why `throttled_loop` inside of `move_on_after` fails to stop
|
3
8
|
|
4
|
-
- Commented out `io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);` in `io_uring_backend_defer_submit_and_await`:
|
5
|
-
- This flag should be set for I/O ops, not for other stuff
|
6
|
-
|
7
9
|
- Override stock `::SizedQueue` impl with Queue with capacity
|
8
10
|
|
9
11
|
- Add support for `break` and `StopIteration` in all loops (with tests)
|
@@ -16,17 +18,46 @@
|
|
16
18
|
- `Fiber#receive_loop` (very little effort, should be implemented in C)
|
17
19
|
|
18
20
|
|
19
|
-
- Add `Backend#splice`, `Backend#
|
21
|
+
- Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
|
20
22
|
|
21
23
|
```ruby
|
22
24
|
def two_way_proxy(socket1, socket2)
|
23
25
|
backend = Thread.current.backend
|
24
|
-
f1 = spin { backend.
|
25
|
-
f2 = spin { backend.
|
26
|
+
f1 = spin { backend.splice_to_eof(socket1, socket2) }
|
27
|
+
f2 = spin { backend.splice_to_eof(socket2, socket1) }
|
26
28
|
Fiber.await(f1, f2)
|
27
29
|
end
|
28
30
|
```
|
29
31
|
|
32
|
+
- Add support for `close` to io_uring backend
|
33
|
+
|
34
|
+
- Add support for submission of multiple requests to io_uring backend:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
Thread.current.backend.submit(
|
38
|
+
[:send, sock, chunk_header(len)],
|
39
|
+
[:splice, file, sock, len]
|
40
|
+
)
|
41
|
+
```
|
42
|
+
|
43
|
+
Full example (for writing chunks from a file to an HTTP response):
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
def serve_io(io)
|
47
|
+
i, o = IO.pipe
|
48
|
+
backend = Thread.current.backend
|
49
|
+
while true
|
50
|
+
len = o.splice(io, 8192)
|
51
|
+
break if len == 0
|
52
|
+
|
53
|
+
backend.submit(
|
54
|
+
[:write, sock, chunk_header(len)],
|
55
|
+
[:splice, i, sock, len]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
30
61
|
- Graceful shutdown again:
|
31
62
|
- What happens to children when doing a graceful shutdown?
|
32
63
|
- What are the implications of passing graceful shutdown flag to children?
|
data/examples/core/forking.rb
CHANGED
@@ -5,7 +5,7 @@ require 'polyphony'
|
|
5
5
|
|
6
6
|
puts "parent pid: #{Process.pid}"
|
7
7
|
|
8
|
-
pid =
|
8
|
+
pid = fork do
|
9
9
|
puts "child pid: #{Process.pid}"
|
10
10
|
|
11
11
|
spin do
|
@@ -20,5 +20,5 @@ end
|
|
20
20
|
puts "got child pid #{pid}"
|
21
21
|
|
22
22
|
puts 'parent waiting for child'
|
23
|
-
|
23
|
+
Polyphony.backend_waitpid(pid)
|
24
24
|
puts 'parent done waiting'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
queue = Queue.new
|
7
|
+
|
8
|
+
4.times { |i|
|
9
|
+
spin_loop {
|
10
|
+
job = queue.pop
|
11
|
+
puts("worker %d job %s" % [i, job.inspect])
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
(1..10).each do |i|
|
16
|
+
queue << "job#{i}"
|
17
|
+
end
|
18
|
+
|
19
|
+
sleep 0.1 until queue.empty?
|
data/examples/io/echo_server.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'localhost/authority'
|
6
|
+
|
7
|
+
authority = Localhost::Authority.fetch
|
8
|
+
opts = {
|
9
|
+
reuse_addr: true,
|
10
|
+
dont_linger: true,
|
11
|
+
secure_context: authority.server_context
|
12
|
+
}
|
13
|
+
|
14
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
|
15
|
+
|
16
|
+
puts 'Serving HTTPS on port 1234'
|
17
|
+
|
18
|
+
spin_loop(interval: 1) { STDOUT << '.' }
|
19
|
+
|
20
|
+
# server.accept_loop do |socket|
|
21
|
+
while (socket = server.accept)
|
22
|
+
spin do
|
23
|
+
while (data = socket.gets("\n", 8192))
|
24
|
+
if data.chomp.empty?
|
25
|
+
socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
|
26
|
+
break
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/examples/io/tcp_proxy.rb
CHANGED
@@ -15,7 +15,7 @@ f1 = spin {
|
|
15
15
|
client1 = server1.accept
|
16
16
|
loop do
|
17
17
|
if client2
|
18
|
-
Thread.current.backend.
|
18
|
+
Thread.current.backend.splice_to_eof(client1, client2)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
}
|
@@ -24,7 +24,7 @@ f2 = spin {
|
|
24
24
|
client2 = server2.accept
|
25
25
|
loop do
|
26
26
|
if client1
|
27
|
-
Thread.current.backend.
|
27
|
+
Thread.current.backend.splice_to_eof(client2, client1)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
}
|
@@ -3,6 +3,8 @@
|
|
3
3
|
#include "ruby.h"
|
4
4
|
#include "ruby/io.h"
|
5
5
|
|
6
|
+
|
7
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
6
8
|
#ifndef __NR_pidfd_open
|
7
9
|
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
8
10
|
#endif
|
@@ -10,6 +12,7 @@
|
|
10
12
|
static int pidfd_open(pid_t pid, unsigned int flags) {
|
11
13
|
return syscall(__NR_pidfd_open, pid, flags);
|
12
14
|
}
|
15
|
+
#endif
|
13
16
|
|
14
17
|
//////////////////////////////////////////////////////////////////////
|
15
18
|
//////////////////////////////////////////////////////////////////////
|
@@ -23,11 +26,11 @@ struct io_internal_read_struct {
|
|
23
26
|
|
24
27
|
#define StringValue(v) rb_string_value(&(v))
|
25
28
|
|
26
|
-
|
29
|
+
int io_setstrbuf(VALUE *str, long len) {
|
27
30
|
#ifdef _WIN32
|
28
31
|
len = (len + 1) & ~1L; /* round up for wide char */
|
29
32
|
#endif
|
30
|
-
if (
|
33
|
+
if (*str == Qnil) {
|
31
34
|
*str = rb_str_new(0, len);
|
32
35
|
return 1;
|
33
36
|
}
|
@@ -52,7 +55,7 @@ inline void io_shrink_read_string(VALUE str, long n) {
|
|
52
55
|
}
|
53
56
|
}
|
54
57
|
|
55
|
-
|
58
|
+
void io_set_read_length(VALUE str, long n, int shrinkable) {
|
56
59
|
if (RSTRING_LEN(str) != n) {
|
57
60
|
rb_str_modify(str);
|
58
61
|
rb_str_set_len(str, n);
|
@@ -67,7 +70,7 @@ inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
|
67
70
|
return rb_default_external_encoding();
|
68
71
|
}
|
69
72
|
|
70
|
-
|
73
|
+
VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
71
74
|
OBJ_TAINT(str);
|
72
75
|
rb_enc_associate(str, io_read_encoding(fptr));
|
73
76
|
return str;
|
@@ -135,9 +138,9 @@ inline double current_time() {
|
|
135
138
|
}
|
136
139
|
|
137
140
|
inline VALUE backend_timeout_exception(VALUE exception) {
|
138
|
-
if (
|
141
|
+
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
139
142
|
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
140
|
-
else if (
|
143
|
+
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
141
144
|
return rb_funcall(exception, ID_new, 0);
|
142
145
|
else
|
143
146
|
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
@@ -154,3 +157,23 @@ VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
|
154
157
|
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
155
158
|
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
156
159
|
}
|
160
|
+
|
161
|
+
static VALUE empty_string = Qnil;
|
162
|
+
|
163
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
164
|
+
switch (RARRAY_LEN(ary)) {
|
165
|
+
case 0:
|
166
|
+
return Qnil;
|
167
|
+
case 1:
|
168
|
+
return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
|
169
|
+
default:
|
170
|
+
if (empty_string == Qnil) {
|
171
|
+
empty_string = rb_str_new_literal("");
|
172
|
+
rb_global_variable(&empty_string);
|
173
|
+
}
|
174
|
+
VALUE joined = rb_ary_join(ary, empty_string);
|
175
|
+
VALUE result = Backend_send(self, io, joined, flags);
|
176
|
+
RB_GC_GUARD(joined);
|
177
|
+
return result;
|
178
|
+
}
|
179
|
+
}
|
@@ -16,11 +16,37 @@
|
|
16
16
|
|
17
17
|
#include "polyphony.h"
|
18
18
|
#include "../liburing/liburing.h"
|
19
|
-
#include "ruby/thread.h"
|
20
19
|
#include "backend_io_uring_context.h"
|
20
|
+
#include "ruby/thread.h"
|
21
|
+
#include "ruby/io.h"
|
21
22
|
|
22
23
|
VALUE SYM_io_uring;
|
23
24
|
|
25
|
+
#ifdef POLYPHONY_UNSET_NONBLOCK
|
26
|
+
ID ID_ivar_is_nonblocking;
|
27
|
+
|
28
|
+
// One of the changes introduced in Ruby 3.0 as part of the work on the
|
29
|
+
// FiberScheduler interface is that all created sockets are marked as
|
30
|
+
// non-blocking. This prevents the io_uring backend from working correctly,
|
31
|
+
// since it will return an EAGAIN error just like a normal syscall. So here
|
32
|
+
// instead of setting O_NONBLOCK (which is required for the libev backend), we
|
33
|
+
// unset it.
|
34
|
+
inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
|
35
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
36
|
+
if (is_nonblocking == Qfalse) return;
|
37
|
+
|
38
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qfalse);
|
39
|
+
|
40
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
41
|
+
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
42
|
+
oflags &= !O_NONBLOCK;
|
43
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
44
|
+
}
|
45
|
+
#else
|
46
|
+
// NOP
|
47
|
+
#define io_unset_nonblock(fptr, io)
|
48
|
+
#endif
|
49
|
+
|
24
50
|
typedef struct Backend_t {
|
25
51
|
// common fields
|
26
52
|
unsigned int currently_polling;
|
@@ -124,7 +150,7 @@ static inline bool cq_ring_needs_flush(struct io_uring *ring) {
|
|
124
150
|
return IO_URING_READ_ONCE(*ring->sq.kflags) & IORING_SQ_CQ_OVERFLOW;
|
125
151
|
}
|
126
152
|
|
127
|
-
void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
|
153
|
+
static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
|
128
154
|
op_context_t *ctx = io_uring_cqe_get_data(cqe);
|
129
155
|
if (!ctx) return;
|
130
156
|
|
@@ -143,7 +169,7 @@ void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *bac
|
|
143
169
|
}
|
144
170
|
|
145
171
|
// adapted from io_uring_peek_batch_cqe in queue.c
|
146
|
-
// this peeks at cqes and
|
172
|
+
// this peeks at cqes and handles each available cqe
|
147
173
|
void io_uring_backend_handle_ready_cqes(Backend_t *backend) {
|
148
174
|
struct io_uring *ring = &backend->ring;
|
149
175
|
bool overflow_checked = false;
|
@@ -289,8 +315,8 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
289
315
|
Backend_t *backend;
|
290
316
|
rb_io_t *fptr;
|
291
317
|
long dynamic_len = length == Qnil;
|
292
|
-
long
|
293
|
-
int shrinkable = io_setstrbuf(&str,
|
318
|
+
long buffer_size = dynamic_len ? 4096 : NUM2INT(length);
|
319
|
+
int shrinkable = io_setstrbuf(&str, buffer_size);
|
294
320
|
char *buf = RSTRING_PTR(str);
|
295
321
|
long total = 0;
|
296
322
|
int read_to_eof = RTEST(to_eof);
|
@@ -300,6 +326,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
300
326
|
if (underlying_io != Qnil) io = underlying_io;
|
301
327
|
GetOpenFile(io, fptr);
|
302
328
|
rb_io_check_byte_readable(fptr);
|
329
|
+
io_unset_nonblock(fptr, io);
|
303
330
|
rectify_io_file_pos(fptr);
|
304
331
|
OBJ_TAINT(str);
|
305
332
|
|
@@ -307,7 +334,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
307
334
|
VALUE resume_value = Qnil;
|
308
335
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
|
309
336
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
310
|
-
io_uring_prep_read(sqe, fptr->fd, buf,
|
337
|
+
io_uring_prep_read(sqe, fptr->fd, buf, buffer_size - total, -1);
|
311
338
|
|
312
339
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
313
340
|
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
@@ -323,14 +350,15 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
323
350
|
total += result;
|
324
351
|
if (!read_to_eof) break;
|
325
352
|
|
326
|
-
if (total ==
|
353
|
+
if (total == buffer_size) {
|
327
354
|
if (!dynamic_len) break;
|
328
355
|
|
356
|
+
// resize buffer
|
329
357
|
rb_str_resize(str, total);
|
330
|
-
rb_str_modify_expand(str,
|
358
|
+
rb_str_modify_expand(str, buffer_size);
|
331
359
|
buf = RSTRING_PTR(str) + total;
|
332
360
|
shrinkable = 0;
|
333
|
-
|
361
|
+
buffer_size += buffer_size;
|
334
362
|
}
|
335
363
|
else buf += result;
|
336
364
|
}
|
@@ -360,6 +388,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
360
388
|
if (underlying_io != Qnil) io = underlying_io;
|
361
389
|
GetOpenFile(io, fptr);
|
362
390
|
rb_io_check_byte_readable(fptr);
|
391
|
+
io_unset_nonblock(fptr, io);
|
363
392
|
rectify_io_file_pos(fptr);
|
364
393
|
|
365
394
|
while (1) {
|
@@ -406,6 +435,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
406
435
|
if (underlying_io != Qnil) io = underlying_io;
|
407
436
|
GetOpenFile(io, fptr);
|
408
437
|
rb_io_check_byte_readable(fptr);
|
438
|
+
io_unset_nonblock(fptr, io);
|
409
439
|
rectify_io_file_pos(fptr);
|
410
440
|
|
411
441
|
while (1) {
|
@@ -445,6 +475,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
445
475
|
GetBackend(self, backend);
|
446
476
|
io = rb_io_get_write_io(io);
|
447
477
|
GetOpenFile(io, fptr);
|
478
|
+
io_unset_nonblock(fptr, io);
|
448
479
|
|
449
480
|
char *buf = StringValuePtr(str);
|
450
481
|
long len = RSTRING_LEN(str);
|
@@ -488,6 +519,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
488
519
|
GetBackend(self, backend);
|
489
520
|
io = rb_io_get_write_io(io);
|
490
521
|
GetOpenFile(io, fptr);
|
522
|
+
io_unset_nonblock(fptr, io);
|
491
523
|
|
492
524
|
iov = malloc(iov_count * sizeof(struct iovec));
|
493
525
|
for (int i = 0; i < argc; i++) {
|
@@ -567,6 +599,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
|
567
599
|
if (underlying_io != Qnil) io = underlying_io;
|
568
600
|
GetOpenFile(io, fptr);
|
569
601
|
rb_io_check_byte_readable(fptr);
|
602
|
+
io_unset_nonblock(fptr, io);
|
570
603
|
rectify_io_file_pos(fptr);
|
571
604
|
OBJ_TAINT(str);
|
572
605
|
|
@@ -614,6 +647,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
614
647
|
if (underlying_io != Qnil) io = underlying_io;
|
615
648
|
GetOpenFile(io, fptr);
|
616
649
|
rb_io_check_byte_readable(fptr);
|
650
|
+
io_unset_nonblock(fptr, io);
|
617
651
|
rectify_io_file_pos(fptr);
|
618
652
|
|
619
653
|
while (1) {
|
@@ -659,6 +693,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
659
693
|
if (underlying_io != Qnil) io = underlying_io;
|
660
694
|
GetOpenFile(io, fptr);
|
661
695
|
rb_io_check_byte_readable(fptr);
|
696
|
+
io_unset_nonblock(fptr, io);
|
662
697
|
rectify_io_file_pos(fptr);
|
663
698
|
|
664
699
|
while (1) {
|
@@ -687,7 +722,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
687
722
|
return io;
|
688
723
|
}
|
689
724
|
|
690
|
-
VALUE Backend_send(VALUE self, VALUE io, VALUE str) {
|
725
|
+
VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
691
726
|
Backend_t *backend;
|
692
727
|
rb_io_t *fptr;
|
693
728
|
VALUE underlying_io;
|
@@ -697,16 +732,18 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str) {
|
|
697
732
|
GetBackend(self, backend);
|
698
733
|
io = rb_io_get_write_io(io);
|
699
734
|
GetOpenFile(io, fptr);
|
735
|
+
io_unset_nonblock(fptr, io);
|
700
736
|
|
701
737
|
char *buf = StringValuePtr(str);
|
702
738
|
long len = RSTRING_LEN(str);
|
703
739
|
long left = len;
|
740
|
+
int flags_int = NUM2INT(flags);
|
704
741
|
|
705
742
|
while (left > 0) {
|
706
743
|
VALUE resume_value = Qnil;
|
707
744
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SEND);
|
708
745
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
709
|
-
io_uring_prep_send(sqe, fptr->fd, buf, left,
|
746
|
+
io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
|
710
747
|
|
711
748
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
712
749
|
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
@@ -734,6 +771,8 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
734
771
|
if (underlying_sock != Qnil) server_socket = underlying_sock;
|
735
772
|
|
736
773
|
GetOpenFile(server_socket, fptr);
|
774
|
+
io_unset_nonblock(fptr, server_socket);
|
775
|
+
|
737
776
|
while (1) {
|
738
777
|
VALUE resume_value = Qnil;
|
739
778
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_ACCEPT);
|
@@ -787,6 +826,60 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
787
826
|
return self;
|
788
827
|
}
|
789
828
|
|
829
|
+
VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen, int loop) {
|
830
|
+
rb_io_t *src_fptr;
|
831
|
+
rb_io_t *dest_fptr;
|
832
|
+
VALUE underlying_io;
|
833
|
+
int total = 0;
|
834
|
+
|
835
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
836
|
+
if (underlying_io != Qnil) src = underlying_io;
|
837
|
+
GetOpenFile(src, src_fptr);
|
838
|
+
io_unset_nonblock(src_fptr, src);
|
839
|
+
|
840
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
841
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
842
|
+
dest = rb_io_get_write_io(dest);
|
843
|
+
GetOpenFile(dest, dest_fptr);
|
844
|
+
io_unset_nonblock(dest_fptr, dest);
|
845
|
+
|
846
|
+
VALUE resume_value = Qnil;
|
847
|
+
|
848
|
+
while (1) {
|
849
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SPLICE);
|
850
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
851
|
+
io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
|
852
|
+
|
853
|
+
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
854
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
855
|
+
RAISE_IF_EXCEPTION(resume_value);
|
856
|
+
if (!ctx->completed) return resume_value;
|
857
|
+
|
858
|
+
if (result < 0)
|
859
|
+
rb_syserr_fail(-result, strerror(-result));
|
860
|
+
|
861
|
+
total += result;
|
862
|
+
if (result == 0 || !loop) return INT2NUM(total);
|
863
|
+
}
|
864
|
+
|
865
|
+
RB_GC_GUARD(resume_value);
|
866
|
+
}
|
867
|
+
|
868
|
+
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
869
|
+
Backend_t *backend;
|
870
|
+
GetBackend(self, backend);
|
871
|
+
|
872
|
+
return io_uring_backend_splice(backend, src, dest, maxlen, 0);
|
873
|
+
}
|
874
|
+
|
875
|
+
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
|
876
|
+
Backend_t *backend;
|
877
|
+
GetBackend(self, backend);
|
878
|
+
|
879
|
+
return io_uring_backend_splice(backend, src, dest, chunksize, 1);
|
880
|
+
}
|
881
|
+
|
882
|
+
|
790
883
|
VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
791
884
|
Backend_t *backend;
|
792
885
|
rb_io_t *fptr;
|
@@ -797,6 +890,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
797
890
|
|
798
891
|
GetBackend(self, backend);
|
799
892
|
GetOpenFile(sock, fptr);
|
893
|
+
io_unset_nonblock(fptr, sock);
|
800
894
|
|
801
895
|
addr.sin_family = AF_INET;
|
802
896
|
addr.sin_addr.s_addr = inet_addr(host_buf);
|
@@ -823,6 +917,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
823
917
|
if (underlying_io != Qnil) io = underlying_io;
|
824
918
|
GetBackend(self, backend);
|
825
919
|
GetOpenFile(io, fptr);
|
920
|
+
io_unset_nonblock(fptr, io);
|
826
921
|
|
827
922
|
VALUE resume_value = io_uring_backend_wait_fd(backend, fptr->fd, RTEST(write));
|
828
923
|
RAISE_IF_EXCEPTION(resume_value);
|
@@ -997,7 +1092,7 @@ VALUE Backend_kind(VALUE self) {
|
|
997
1092
|
}
|
998
1093
|
|
999
1094
|
void Init_Backend() {
|
1000
|
-
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend",
|
1095
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
1001
1096
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
1002
1097
|
|
1003
1098
|
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
@@ -1006,26 +1101,33 @@ void Init_Backend() {
|
|
1006
1101
|
|
1007
1102
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
1008
1103
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1104
|
+
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1009
1105
|
|
1106
|
+
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1107
|
+
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
1108
|
+
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
1109
|
+
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
1010
1110
|
rb_define_method(cBackend, "read", Backend_read, 4);
|
1011
1111
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
1012
|
-
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
1013
|
-
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1014
1112
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
1015
|
-
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
1016
1113
|
rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
|
1017
|
-
rb_define_method(cBackend, "
|
1018
|
-
rb_define_method(cBackend, "
|
1019
|
-
rb_define_method(cBackend, "
|
1020
|
-
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
1021
|
-
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
1114
|
+
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
1115
|
+
rb_define_method(cBackend, "send", Backend_send, 3);
|
1116
|
+
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1022
1117
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1023
|
-
rb_define_method(cBackend, "
|
1118
|
+
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1119
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1024
1120
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1025
|
-
rb_define_method(cBackend, "
|
1121
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
1026
1122
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
1123
|
+
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
1124
|
+
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
1125
|
+
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1027
1126
|
|
1028
|
-
|
1127
|
+
|
1128
|
+
#ifdef POLYPHONY_UNSET_NONBLOCK
|
1129
|
+
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1130
|
+
#endif
|
1029
1131
|
|
1030
1132
|
SYM_io_uring = ID2SYM(rb_intern("io_uring"));
|
1031
1133
|
}
|