polyphony 0.50.1 → 0.53.2
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 +1 -1
- data/CHANGELOG.md +23 -0
- data/Gemfile.lock +7 -68
- data/TODO.md +37 -6
- data/examples/core/forking.rb +2 -2
- data/examples/io/echo_server.rb +1 -0
- data/examples/io/tcp_proxy.rb +2 -2
- data/ext/polyphony/backend_common.h +36 -6
- data/ext/polyphony/backend_io_uring.c +216 -21
- 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 +362 -20
- 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 +4 -4
- data/lib/polyphony/core/global_api.rb +5 -5
- data/lib/polyphony/core/sync.rb +1 -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 +21 -22
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +56 -47
- 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 +84 -1
- data/test/test_kernel.rb +1 -1
- data/test/test_signal.rb +1 -1
- data/test/test_socket.rb +61 -0
- data/test/test_thread.rb +4 -0
- data/test/test_timer.rb +1 -1
- metadata +19 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d656c0aad868a468632949355b617821b6a66aa4da69a994ea7afbc4af46e97b
|
4
|
+
data.tar.gz: edc45b184b0b50940925a1a1db56b0106ded10906ca181513e38450bc320e828
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f346672c82eee4055ec6c8f7fc1103850fd622a031ebf1c096e9ab130e3aa0b37c559d2b2f953f6b1bd721e4421199800fc0de54993a0c802ddadfa873f35030
|
7
|
+
data.tar.gz: 8273d691c2ed19ca04207380ea99392c7f65fefe6600c4a9bf9cc20a802f41e34b471e3af8d5adccf29c783c2f0c17610896fae2c183ac26b601c34dc27a5ce2
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
## 0.53.2
|
2
|
+
|
3
|
+
- Remove `splice` methods on libev backend on non-Linux OS (#43)
|
4
|
+
|
5
|
+
## 0.53.0
|
6
|
+
|
7
|
+
- Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
|
8
|
+
|
9
|
+
## 0.52.0
|
10
|
+
|
11
|
+
- Polyphony is now compatible with Ruby 3.0
|
12
|
+
- Add `Backend#sendv` method for sending multiple strings
|
13
|
+
- Accept flags argument in `Backend#send` (#48)
|
14
|
+
- Fix io_uring backend on Ruby 3.0 (#47)
|
15
|
+
- Implement C-based public backend API: `Polyphony.backend_XXXX` methods
|
16
|
+
- libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
|
17
|
+
- Use `:call` as default method in `#feed_loop`
|
18
|
+
|
19
|
+
## 0.51.0
|
20
|
+
|
21
|
+
- Implement `IO#feed_loop`, `Socket#feed_loop`
|
22
|
+
- Fix error handling in `Process.kill_and_await`
|
23
|
+
|
1
24
|
## 0.50.1
|
2
25
|
|
3
26
|
- Set `IOSQE_ASYNC` flag in io_uring backend
|
data/Gemfile.lock
CHANGED
@@ -1,101 +1,50 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.53.2)
|
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
|
-
just-the-docs (0.3.0)
|
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)
|
65
20
|
method_source (1.0.0)
|
66
21
|
mime-types (3.3.1)
|
67
22
|
mime-types-data (~> 3.2015)
|
68
23
|
mime-types-data (3.2020.0512)
|
69
|
-
minitest (5.
|
24
|
+
minitest (5.14.4)
|
70
25
|
minitest-reporters (1.4.2)
|
71
26
|
ansi
|
72
27
|
builder
|
73
28
|
minitest (>= 5.0)
|
74
29
|
ruby-progressbar
|
30
|
+
msgpack (1.4.2)
|
75
31
|
multi_xml (0.6.0)
|
76
32
|
mysql2 (0.5.3)
|
77
33
|
parallel (1.19.1)
|
78
34
|
parser (2.7.0.2)
|
79
35
|
ast (~> 2.4.0)
|
80
|
-
pathutil (0.16.2)
|
81
|
-
forwardable-extended (~> 2.6)
|
82
36
|
pg (1.1.4)
|
83
37
|
pry (0.13.1)
|
84
38
|
coderay (~> 1.1)
|
85
39
|
method_source (~> 1.0)
|
86
|
-
public_suffix (4.0.3)
|
87
40
|
rack (2.2.3)
|
88
41
|
rainbow (3.0.0)
|
89
|
-
rake (
|
42
|
+
rake (13.0.3)
|
90
43
|
rake-compiler (1.1.1)
|
91
44
|
rake
|
92
|
-
rb-fsevent (0.10.3)
|
93
|
-
rb-inotify (0.10.1)
|
94
|
-
ffi (~> 1.0)
|
95
45
|
redis (4.1.0)
|
96
46
|
regexp_parser (1.7.1)
|
97
47
|
rexml (3.2.4)
|
98
|
-
rouge (3.15.0)
|
99
48
|
rubocop (0.85.1)
|
100
49
|
parallel (~> 1.10)
|
101
50
|
parser (>= 2.7.0.1)
|
@@ -108,13 +57,6 @@ GEM
|
|
108
57
|
rubocop-ast (0.0.3)
|
109
58
|
parser (>= 2.7.0.1)
|
110
59
|
ruby-progressbar (1.10.1)
|
111
|
-
rubyzip (2.0.0)
|
112
|
-
safe_yaml (1.0.5)
|
113
|
-
sass (3.7.4)
|
114
|
-
sass-listen (~> 4.0.0)
|
115
|
-
sass-listen (4.0.0)
|
116
|
-
rb-fsevent (~> 0.9, >= 0.9.4)
|
117
|
-
rb-inotify (~> 0.9, >= 0.9.7)
|
118
60
|
sequel (5.34.0)
|
119
61
|
simplecov (0.17.1)
|
120
62
|
docile (~> 1.1)
|
@@ -130,12 +72,9 @@ DEPENDENCIES
|
|
130
72
|
hiredis (= 0.6.3)
|
131
73
|
http_parser.rb (~> 0.6.0)
|
132
74
|
httparty (= 0.17.1)
|
133
|
-
|
134
|
-
jekyll-remote-theme (~> 0.4.1)
|
135
|
-
jekyll-seo-tag (~> 2.6.1)
|
136
|
-
just-the-docs (~> 0.3.0)
|
137
|
-
minitest (= 5.13.0)
|
75
|
+
minitest (= 5.14.4)
|
138
76
|
minitest-reporters (= 1.4.2)
|
77
|
+
msgpack (= 1.4.2)
|
139
78
|
mysql2 (= 0.5.3)
|
140
79
|
pg (= 1.1.4)
|
141
80
|
polyphony!
|
@@ -148,4 +87,4 @@ DEPENDENCIES
|
|
148
87
|
simplecov (= 0.17.1)
|
149
88
|
|
150
89
|
BUNDLED WITH
|
151
|
-
2.
|
90
|
+
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'
|
data/examples/io/echo_server.rb
CHANGED
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;
|
@@ -107,6 +110,13 @@ inline VALUE backend_snooze() {
|
|
107
110
|
READ_LOOP_PREPARE_STR(); \
|
108
111
|
}
|
109
112
|
|
113
|
+
#define READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id) { \
|
114
|
+
io_set_read_length(str, total, shrinkable); \
|
115
|
+
io_enc_str(str, fptr); \
|
116
|
+
rb_funcall_passing_block(receiver, method_id, 1, &str); \
|
117
|
+
READ_LOOP_PREPARE_STR(); \
|
118
|
+
}
|
119
|
+
|
110
120
|
inline void rectify_io_file_pos(rb_io_t *fptr) {
|
111
121
|
// Apparently after reopening a closed file, the file position is not reset,
|
112
122
|
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
@@ -128,9 +138,9 @@ inline double current_time() {
|
|
128
138
|
}
|
129
139
|
|
130
140
|
inline VALUE backend_timeout_exception(VALUE exception) {
|
131
|
-
if (
|
141
|
+
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
132
142
|
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
133
|
-
else if (
|
143
|
+
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
134
144
|
return rb_funcall(exception, ID_new, 0);
|
135
145
|
else
|
136
146
|
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
@@ -147,3 +157,23 @@ VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
|
147
157
|
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
148
158
|
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
149
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) {
|
@@ -389,6 +418,53 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
389
418
|
return io;
|
390
419
|
}
|
391
420
|
|
421
|
+
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
422
|
+
Backend_t *backend;
|
423
|
+
rb_io_t *fptr;
|
424
|
+
VALUE str;
|
425
|
+
long total;
|
426
|
+
long len = 8192;
|
427
|
+
int shrinkable;
|
428
|
+
char *buf;
|
429
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
430
|
+
ID method_id = SYM2ID(method);
|
431
|
+
|
432
|
+
READ_LOOP_PREPARE_STR();
|
433
|
+
|
434
|
+
GetBackend(self, backend);
|
435
|
+
if (underlying_io != Qnil) io = underlying_io;
|
436
|
+
GetOpenFile(io, fptr);
|
437
|
+
rb_io_check_byte_readable(fptr);
|
438
|
+
io_unset_nonblock(fptr, io);
|
439
|
+
rectify_io_file_pos(fptr);
|
440
|
+
|
441
|
+
while (1) {
|
442
|
+
VALUE resume_value = Qnil;
|
443
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
|
444
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
445
|
+
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
446
|
+
|
447
|
+
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
448
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
449
|
+
RAISE_IF_EXCEPTION(resume_value);
|
450
|
+
if (!ctx->completed) return resume_value;
|
451
|
+
RB_GC_GUARD(resume_value);
|
452
|
+
|
453
|
+
if (result < 0)
|
454
|
+
rb_syserr_fail(-result, strerror(-result));
|
455
|
+
else if (!result)
|
456
|
+
break; // EOF
|
457
|
+
else {
|
458
|
+
total = result;
|
459
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
460
|
+
}
|
461
|
+
}
|
462
|
+
|
463
|
+
RB_GC_GUARD(str);
|
464
|
+
|
465
|
+
return io;
|
466
|
+
}
|
467
|
+
|
392
468
|
VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
393
469
|
Backend_t *backend;
|
394
470
|
rb_io_t *fptr;
|
@@ -399,6 +475,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
399
475
|
GetBackend(self, backend);
|
400
476
|
io = rb_io_get_write_io(io);
|
401
477
|
GetOpenFile(io, fptr);
|
478
|
+
io_unset_nonblock(fptr, io);
|
402
479
|
|
403
480
|
char *buf = StringValuePtr(str);
|
404
481
|
long len = RSTRING_LEN(str);
|
@@ -442,6 +519,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
442
519
|
GetBackend(self, backend);
|
443
520
|
io = rb_io_get_write_io(io);
|
444
521
|
GetOpenFile(io, fptr);
|
522
|
+
io_unset_nonblock(fptr, io);
|
445
523
|
|
446
524
|
iov = malloc(iov_count * sizeof(struct iovec));
|
447
525
|
for (int i = 0; i < argc; i++) {
|
@@ -521,6 +599,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
|
521
599
|
if (underlying_io != Qnil) io = underlying_io;
|
522
600
|
GetOpenFile(io, fptr);
|
523
601
|
rb_io_check_byte_readable(fptr);
|
602
|
+
io_unset_nonblock(fptr, io);
|
524
603
|
rectify_io_file_pos(fptr);
|
525
604
|
OBJ_TAINT(str);
|
526
605
|
|
@@ -568,6 +647,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
568
647
|
if (underlying_io != Qnil) io = underlying_io;
|
569
648
|
GetOpenFile(io, fptr);
|
570
649
|
rb_io_check_byte_readable(fptr);
|
650
|
+
io_unset_nonblock(fptr, io);
|
571
651
|
rectify_io_file_pos(fptr);
|
572
652
|
|
573
653
|
while (1) {
|
@@ -596,7 +676,53 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
596
676
|
return io;
|
597
677
|
}
|
598
678
|
|
599
|
-
VALUE
|
679
|
+
VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
680
|
+
Backend_t *backend;
|
681
|
+
rb_io_t *fptr;
|
682
|
+
VALUE str;
|
683
|
+
long total;
|
684
|
+
long len = 8192;
|
685
|
+
int shrinkable;
|
686
|
+
char *buf;
|
687
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
688
|
+
ID method_id = SYM2ID(method);
|
689
|
+
|
690
|
+
READ_LOOP_PREPARE_STR();
|
691
|
+
|
692
|
+
GetBackend(self, backend);
|
693
|
+
if (underlying_io != Qnil) io = underlying_io;
|
694
|
+
GetOpenFile(io, fptr);
|
695
|
+
rb_io_check_byte_readable(fptr);
|
696
|
+
io_unset_nonblock(fptr, io);
|
697
|
+
rectify_io_file_pos(fptr);
|
698
|
+
|
699
|
+
while (1) {
|
700
|
+
VALUE resume_value = Qnil;
|
701
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
|
702
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
703
|
+
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
704
|
+
|
705
|
+
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
706
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
707
|
+
RAISE_IF_EXCEPTION(resume_value);
|
708
|
+
if (!ctx->completed) return resume_value;
|
709
|
+
RB_GC_GUARD(resume_value);
|
710
|
+
|
711
|
+
if (result < 0)
|
712
|
+
rb_syserr_fail(-result, strerror(-result));
|
713
|
+
else if (!result)
|
714
|
+
break; // EOF
|
715
|
+
else {
|
716
|
+
total = result;
|
717
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
718
|
+
}
|
719
|
+
}
|
720
|
+
|
721
|
+
RB_GC_GUARD(str);
|
722
|
+
return io;
|
723
|
+
}
|
724
|
+
|
725
|
+
VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
600
726
|
Backend_t *backend;
|
601
727
|
rb_io_t *fptr;
|
602
728
|
VALUE underlying_io;
|
@@ -606,16 +732,18 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str) {
|
|
606
732
|
GetBackend(self, backend);
|
607
733
|
io = rb_io_get_write_io(io);
|
608
734
|
GetOpenFile(io, fptr);
|
735
|
+
io_unset_nonblock(fptr, io);
|
609
736
|
|
610
737
|
char *buf = StringValuePtr(str);
|
611
738
|
long len = RSTRING_LEN(str);
|
612
739
|
long left = len;
|
740
|
+
int flags_int = NUM2INT(flags);
|
613
741
|
|
614
742
|
while (left > 0) {
|
615
743
|
VALUE resume_value = Qnil;
|
616
744
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SEND);
|
617
745
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
618
|
-
io_uring_prep_send(sqe, fptr->fd, buf, left,
|
746
|
+
io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
|
619
747
|
|
620
748
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
621
749
|
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
@@ -643,6 +771,8 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
643
771
|
if (underlying_sock != Qnil) server_socket = underlying_sock;
|
644
772
|
|
645
773
|
GetOpenFile(server_socket, fptr);
|
774
|
+
io_unset_nonblock(fptr, server_socket);
|
775
|
+
|
646
776
|
while (1) {
|
647
777
|
VALUE resume_value = Qnil;
|
648
778
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_ACCEPT);
|
@@ -696,6 +826,60 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
696
826
|
return self;
|
697
827
|
}
|
698
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
|
+
|
699
883
|
VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
700
884
|
Backend_t *backend;
|
701
885
|
rb_io_t *fptr;
|
@@ -706,6 +890,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
706
890
|
|
707
891
|
GetBackend(self, backend);
|
708
892
|
GetOpenFile(sock, fptr);
|
893
|
+
io_unset_nonblock(fptr, sock);
|
709
894
|
|
710
895
|
addr.sin_family = AF_INET;
|
711
896
|
addr.sin_addr.s_addr = inet_addr(host_buf);
|
@@ -732,6 +917,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
732
917
|
if (underlying_io != Qnil) io = underlying_io;
|
733
918
|
GetBackend(self, backend);
|
734
919
|
GetOpenFile(io, fptr);
|
920
|
+
io_unset_nonblock(fptr, io);
|
735
921
|
|
736
922
|
VALUE resume_value = io_uring_backend_wait_fd(backend, fptr->fd, RTEST(write));
|
737
923
|
RAISE_IF_EXCEPTION(resume_value);
|
@@ -906,7 +1092,7 @@ VALUE Backend_kind(VALUE self) {
|
|
906
1092
|
}
|
907
1093
|
|
908
1094
|
void Init_Backend() {
|
909
|
-
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend",
|
1095
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
910
1096
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
911
1097
|
|
912
1098
|
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
@@ -915,24 +1101,33 @@ void Init_Backend() {
|
|
915
1101
|
|
916
1102
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
917
1103
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1104
|
+
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
918
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);
|
919
1110
|
rb_define_method(cBackend, "read", Backend_read, 4);
|
920
1111
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
921
|
-
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
922
1112
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
1113
|
+
rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
|
923
1114
|
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
924
|
-
rb_define_method(cBackend, "send", Backend_send,
|
925
|
-
rb_define_method(cBackend, "
|
926
|
-
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
927
|
-
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
928
|
-
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
1115
|
+
rb_define_method(cBackend, "send", Backend_send, 3);
|
1116
|
+
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
929
1117
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
930
|
-
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);
|
931
1120
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
932
|
-
rb_define_method(cBackend, "
|
1121
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
933
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);
|
934
1126
|
|
935
|
-
|
1127
|
+
|
1128
|
+
#ifdef POLYPHONY_UNSET_NONBLOCK
|
1129
|
+
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1130
|
+
#endif
|
936
1131
|
|
937
1132
|
SYM_io_uring = ID2SYM(rb_intern("io_uring"));
|
938
1133
|
}
|