polyphony 0.52.0 → 0.55.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 +1 -1
- data/.gitignore +3 -1
- data/CHANGELOG.md +45 -22
- data/Gemfile.lock +3 -1
- data/TODO.md +36 -11
- 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/stdio.rb +8 -0
- data/examples/io/tcp_proxy.rb +2 -2
- data/ext/polyphony/backend_common.h +10 -10
- data/ext/polyphony/backend_io_uring.c +251 -85
- data/ext/polyphony/backend_io_uring_context.c +15 -2
- data/ext/polyphony/backend_io_uring_context.h +12 -11
- data/ext/polyphony/backend_libev.c +165 -24
- data/ext/polyphony/extconf.rb +3 -1
- data/ext/polyphony/polyphony.c +10 -2
- data/ext/polyphony/polyphony.h +5 -0
- data/ext/polyphony/polyphony_ext.c +3 -0
- data/ext/polyphony/runqueue.c +29 -1
- data/ext/polyphony/socket_extensions.c +33 -0
- data/ext/polyphony/thread.c +22 -6
- data/lib/polyphony/core/sync.rb +8 -0
- data/lib/polyphony/extensions/io.rb +8 -0
- data/lib/polyphony/extensions/openssl.rb +24 -17
- data/lib/polyphony/extensions/socket.rb +30 -35
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -0
- data/test/helper.rb +3 -3
- data/test/test_backend.rb +164 -1
- data/test/test_fiber.rb +0 -1
- data/test/test_io.rb +6 -3
- data/test/test_signal.rb +1 -1
- data/test/test_sync.rb +43 -0
- data/test/test_thread.rb +4 -0
- data/test/test_thread_pool.rb +1 -1
- data/test/test_timer.rb +13 -7
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f0f6698a072e4ba2913477d20f5884d5d7579b99cca1db0f2ffa3aeca42ebcc
|
4
|
+
data.tar.gz: 97fec9986f6693d685bfb0c1745995c8a85413a9b6cc1a1ff69393d55a5f3b74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 053beffc7c27658d129002b5efc29f64c7fcf1ff7fbf3f2eea1d588b2a9e40e89251640bf9bb82ce478837f38c32863987d8ac0af987d374d4ac242d012363dd
|
7
|
+
data.tar.gz: e89df4044f09f4df64fc57622f22e70d932f2f09ea3b2a5c2090698784c286367df43d352231068b8ef96d6a7ba89e5492b4c237c9960aeb5fa79af9ee738768
|
data/.github/workflows/test.yml
CHANGED
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,27 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.55.0 2021-06-17
|
2
|
+
|
3
|
+
- Finish io_uring implementation of Backend#chain
|
4
|
+
- Reimplement io_uring op_context acquire/release algorithm (using ref count)
|
5
|
+
- Fix #gets on sockets
|
6
|
+
- Redesign event anti-starvation mechanism
|
7
|
+
|
8
|
+
## 0.54.0 2021-06-14
|
9
|
+
|
10
|
+
- Implement Mutex#owned?, #locked? (#50)
|
11
|
+
- Fix arity for SSLSocket#peeraddr (#55)
|
12
|
+
- Add missing SSLServer#accept_loop method (#53)
|
13
|
+
- Fix SSLSocket buffering behaviour
|
14
|
+
- Add recv_loop alias for SSLSocket (#54)
|
15
|
+
|
16
|
+
## 0.53.2 2021-05-10
|
17
|
+
|
18
|
+
- Remove `splice` methods on libev backend on non-Linux OS (#43)
|
19
|
+
|
20
|
+
## 0.53.0 2021-04-23
|
21
|
+
|
22
|
+
- Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
|
23
|
+
|
24
|
+
## 0.52.0 2021-02-28
|
2
25
|
|
3
26
|
- Polyphony is now compatible with Ruby 3.0
|
4
27
|
- Add `Backend#sendv` method for sending multiple strings
|
@@ -8,19 +31,19 @@
|
|
8
31
|
- libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
|
9
32
|
- Use `:call` as default method in `#feed_loop`
|
10
33
|
|
11
|
-
## 0.51.0
|
34
|
+
## 0.51.0 2021-02-02
|
12
35
|
|
13
36
|
- Implement `IO#feed_loop`, `Socket#feed_loop`
|
14
37
|
- Fix error handling in `Process.kill_and_await`
|
15
38
|
|
16
|
-
## 0.50.1
|
39
|
+
## 0.50.1 2021-01-31
|
17
40
|
|
18
41
|
- Set `IOSQE_ASYNC` flag in io_uring backend
|
19
42
|
- Fix error handling in `Backend#waitpid`
|
20
43
|
- Reimplement libev backend's `#waitpid` by using pidfd_open (in similar manner
|
21
44
|
to the io_uring backend)
|
22
45
|
|
23
|
-
## 0.50.0
|
46
|
+
## 0.50.0 2021-01-28
|
24
47
|
|
25
48
|
- Use `Process::CLOCK_MONOTONIC` in Timer
|
26
49
|
- Add `Timer#sleep`, `Timer#after`, `Timer#every`
|
@@ -28,50 +51,50 @@
|
|
28
51
|
- Add `Thread#fiber_index_of` method
|
29
52
|
- Use `Backend#wait_event` in `Fiber#await`
|
30
53
|
|
31
|
-
## 0.49.2
|
54
|
+
## 0.49.2 2021-01-19
|
32
55
|
|
33
56
|
- Fix hang with 100s or more child fibers when terminating
|
34
57
|
- Fix double pending_count increment in io_uring backend
|
35
58
|
|
36
|
-
## 0.49.1
|
59
|
+
## 0.49.1 2021-01-13
|
37
60
|
|
38
61
|
- Use `TCPSocket` instead of `Socket` in `Net.tcp_connect`
|
39
62
|
- Catch `Errno::ERSCH` in `Process.kill_and_await`
|
40
63
|
- Set io_uring queue size to 2048
|
41
64
|
|
42
|
-
## 0.49.0
|
65
|
+
## 0.49.0 2021-01-11
|
43
66
|
|
44
67
|
- Implement `Polyphony::Timer` for performant timeouts
|
45
68
|
|
46
|
-
## 0.48.0
|
69
|
+
## 0.48.0 2021-01-05
|
47
70
|
|
48
71
|
- Implement graceful shutdown
|
49
72
|
- Add support for `break` / `StopIteration` in `spin_loop`
|
50
73
|
- Fix `IO#gets`, `IO#readpartial`
|
51
74
|
|
52
|
-
## 0.47.5.1
|
75
|
+
## 0.47.5.1 2020-11-20
|
53
76
|
|
54
77
|
- Add missing `Socket#accept_loop` method
|
55
78
|
|
56
|
-
## 0.47.5
|
79
|
+
## 0.47.5 2020-11-20
|
57
80
|
|
58
81
|
- Add `socket_class` argument to `Backend#accept`, `Backend#accept_loop`
|
59
82
|
- Fix `#supervise` to stop when all children fibers are done
|
60
83
|
|
61
|
-
## 0.47.4
|
84
|
+
## 0.47.4 2020-11-14
|
62
85
|
|
63
86
|
- Add support for Unix sockets
|
64
87
|
|
65
|
-
## 0.47.3
|
88
|
+
## 0.47.3 2020-11-12
|
66
89
|
|
67
90
|
- Enable I/O in signal handlers (#45)
|
68
91
|
- Accept `:interval` argument in `#spin_loop`
|
69
92
|
|
70
|
-
## 0.47.2
|
93
|
+
## 0.47.2 2020-11-10
|
71
94
|
|
72
95
|
- Fix API compatibility between TCPSocket and IO
|
73
96
|
|
74
|
-
## 0.47.0
|
97
|
+
## 0.47.0 2020-11-10
|
75
98
|
|
76
99
|
- Implement `#spin_scope` used for creating blocking fiber scopes
|
77
100
|
- Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
|
@@ -79,18 +102,18 @@
|
|
79
102
|
- Implement `Backend#timeout` API
|
80
103
|
- Implemented capped queues
|
81
104
|
|
82
|
-
## 0.46.1
|
105
|
+
## 0.46.1 2020-11-04
|
83
106
|
|
84
107
|
- Add `TCPServer#accept_loop`, `OpenSSL::SSL::SSLSocket#accept_loop` method
|
85
108
|
- Fix compilation error on MacOS (#43)
|
86
109
|
- Fix backtrace for `Timeout.timeout`
|
87
110
|
- Add `Backend#timer_loop`
|
88
111
|
|
89
|
-
## 0.46.0
|
112
|
+
## 0.46.0 2020-10-08
|
90
113
|
|
91
114
|
- Implement [io_uring backend](https://github.com/digital-fabric/polyphony/pull/44)
|
92
115
|
|
93
|
-
## 0.45.5
|
116
|
+
## 0.45.5 2020-10-04
|
94
117
|
|
95
118
|
- Fix compilation error (#43)
|
96
119
|
- Add support for resetting move_on_after, cancel_after timeouts
|
@@ -99,22 +122,22 @@
|
|
99
122
|
- Schedule parent with priority on uncaught exception
|
100
123
|
- Fix race condition in `Mutex#synchronize` (#41)
|
101
124
|
|
102
|
-
## 0.45.4
|
125
|
+
## 0.45.4 2020-09-06
|
103
126
|
|
104
127
|
- Improve signal trapping mechanism
|
105
128
|
|
106
|
-
## 0.45.3
|
129
|
+
## 0.45.3 2020-09-02
|
107
130
|
|
108
131
|
- Don't swallow error in `Process#kill_and_await`
|
109
132
|
- Add `Fiber#mailbox` attribute reader
|
110
133
|
- Fix bug in `Fiber.await`
|
111
134
|
- Implement `IO#getc`, `IO#getbyte`
|
112
135
|
|
113
|
-
## 0.45.2
|
136
|
+
## 0.45.2 2020-08-03
|
114
137
|
|
115
138
|
- Rewrite `Fiber#<<`, `Fiber#await`, `Fiber#receive` in C
|
116
139
|
|
117
|
-
## 0.45.1
|
140
|
+
## 0.45.1 2020-08-01
|
118
141
|
|
119
142
|
- Fix Net::HTTP compatibility
|
120
143
|
- Fix fs adapter
|
@@ -124,7 +147,7 @@
|
|
124
147
|
- Cleanup code
|
125
148
|
- Improve support for Ruby 3 keyword args
|
126
149
|
|
127
|
-
## 0.45.0
|
150
|
+
## 0.45.0 2020-07-29
|
128
151
|
|
129
152
|
- Cleanup code
|
130
153
|
- Rename `Agent` to `Backend`
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.55.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -17,6 +17,7 @@ GEM
|
|
17
17
|
mime-types (~> 3.0)
|
18
18
|
multi_xml (>= 0.5.2)
|
19
19
|
json (2.3.0)
|
20
|
+
localhost (1.1.8)
|
20
21
|
method_source (1.0.0)
|
21
22
|
mime-types (3.3.1)
|
22
23
|
mime-types-data (~> 3.2015)
|
@@ -72,6 +73,7 @@ DEPENDENCIES
|
|
72
73
|
hiredis (= 0.6.3)
|
73
74
|
http_parser.rb (~> 0.6.0)
|
74
75
|
httparty (= 0.17.1)
|
76
|
+
localhost (~> 1.1.4)
|
75
77
|
minitest (= 5.14.4)
|
76
78
|
minitest-reporters (= 1.4.2)
|
77
79
|
msgpack (= 1.4.2)
|
data/TODO.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
- libvev Backend_writev (for iov)
|
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
|
6
5
|
|
7
6
|
- Check segfault when resetting a `cancel_after` timeout lots of times at very high rate
|
8
7
|
- Check why `throttled_loop` inside of `move_on_after` fails to stop
|
@@ -11,25 +10,51 @@
|
|
11
10
|
|
12
11
|
- Add support for `break` and `StopIteration` in all loops (with tests)
|
13
12
|
|
14
|
-
- Change `IO#gets` to use `String#split` to cut into lines, much faster (see
|
15
|
-
examples/performance/line_splitting.rb)
|
16
|
-
|
17
13
|
- More tight loops
|
18
14
|
- `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
|
19
15
|
- `Fiber#receive_loop` (very little effort, should be implemented in C)
|
20
16
|
|
21
17
|
|
22
|
-
- Add `Backend#splice`, `Backend#
|
18
|
+
- Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
|
23
19
|
|
24
20
|
```ruby
|
25
21
|
def two_way_proxy(socket1, socket2)
|
26
22
|
backend = Thread.current.backend
|
27
|
-
f1 = spin { backend.
|
28
|
-
f2 = spin { backend.
|
23
|
+
f1 = spin { backend.splice_to_eof(socket1, socket2) }
|
24
|
+
f2 = spin { backend.splice_to_eof(socket2, socket1) }
|
29
25
|
Fiber.await(f1, f2)
|
30
26
|
end
|
31
27
|
```
|
32
28
|
|
29
|
+
- Add support for `close` to io_uring backend
|
30
|
+
|
31
|
+
- Add support for submission of multiple requests to io_uring backend:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
Thread.current.backend.submit(
|
35
|
+
[:send, sock, chunk_header(len)],
|
36
|
+
[:splice, file, sock, len]
|
37
|
+
)
|
38
|
+
```
|
39
|
+
|
40
|
+
Full example (for writing chunks from a file to an HTTP response):
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
def serve_io(io)
|
44
|
+
i, o = IO.pipe
|
45
|
+
backend = Thread.current.backend
|
46
|
+
while true
|
47
|
+
len = o.splice(io, 8192)
|
48
|
+
break if len == 0
|
49
|
+
|
50
|
+
backend.submit(
|
51
|
+
[:write, sock, chunk_header(len)],
|
52
|
+
[:splice, i, sock, len]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
33
58
|
- Graceful shutdown again:
|
34
59
|
- What happens to children when doing a graceful shutdown?
|
35
60
|
- What are the implications of passing graceful shutdown flag to children?
|
@@ -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
|
}
|
@@ -26,7 +26,7 @@ struct io_internal_read_struct {
|
|
26
26
|
|
27
27
|
#define StringValue(v) rb_string_value(&(v))
|
28
28
|
|
29
|
-
int io_setstrbuf(VALUE *str, long len) {
|
29
|
+
inline int io_setstrbuf(VALUE *str, long len) {
|
30
30
|
#ifdef _WIN32
|
31
31
|
len = (len + 1) & ~1L; /* round up for wide char */
|
32
32
|
#endif
|
@@ -55,7 +55,7 @@ inline void io_shrink_read_string(VALUE str, long n) {
|
|
55
55
|
}
|
56
56
|
}
|
57
57
|
|
58
|
-
void io_set_read_length(VALUE str, long n, int shrinkable) {
|
58
|
+
inline void io_set_read_length(VALUE str, long n, int shrinkable) {
|
59
59
|
if (RSTRING_LEN(str) != n) {
|
60
60
|
rb_str_modify(str);
|
61
61
|
rb_str_set_len(str, n);
|
@@ -64,16 +64,16 @@ void io_set_read_length(VALUE str, long n, int shrinkable) {
|
|
64
64
|
}
|
65
65
|
|
66
66
|
inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
if (fptr->encs.enc) {
|
68
|
+
return fptr->encs.enc;
|
69
|
+
}
|
70
|
+
return rb_default_external_encoding();
|
71
71
|
}
|
72
72
|
|
73
|
-
VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
74
|
+
OBJ_TAINT(str);
|
75
|
+
rb_enc_associate(str, io_read_encoding(fptr));
|
76
|
+
return str;
|
77
77
|
}
|
78
78
|
|
79
79
|
//////////////////////////////////////////////////////////////////////
|
@@ -21,6 +21,9 @@
|
|
21
21
|
#include "ruby/io.h"
|
22
22
|
|
23
23
|
VALUE SYM_io_uring;
|
24
|
+
VALUE SYM_send;
|
25
|
+
VALUE SYM_splice;
|
26
|
+
VALUE SYM_write;
|
24
27
|
|
25
28
|
#ifdef POLYPHONY_UNSET_NONBLOCK
|
26
29
|
ID ID_ivar_is_nonblocking;
|
@@ -43,6 +46,7 @@ inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
|
|
43
46
|
fcntl(fptr->fd, F_SETFL, oflags);
|
44
47
|
}
|
45
48
|
#else
|
49
|
+
// NOP
|
46
50
|
#define io_unset_nonblock(fptr, io)
|
47
51
|
#endif
|
48
52
|
|
@@ -50,7 +54,6 @@ typedef struct Backend_t {
|
|
50
54
|
// common fields
|
51
55
|
unsigned int currently_polling;
|
52
56
|
unsigned int pending_count;
|
53
|
-
unsigned int poll_no_wait_count;
|
54
57
|
|
55
58
|
// implementation-specific fields
|
56
59
|
struct io_uring ring;
|
@@ -87,7 +90,6 @@ static VALUE Backend_initialize(VALUE self) {
|
|
87
90
|
|
88
91
|
backend->currently_polling = 0;
|
89
92
|
backend->pending_count = 0;
|
90
|
-
backend->poll_no_wait_count = 0;
|
91
93
|
backend->pending_sqes = 0;
|
92
94
|
backend->prepared_limit = 2048;
|
93
95
|
|
@@ -117,7 +119,6 @@ VALUE Backend_post_fork(VALUE self) {
|
|
117
119
|
context_store_free(&backend->store);
|
118
120
|
backend->currently_polling = 0;
|
119
121
|
backend->pending_count = 0;
|
120
|
-
backend->poll_no_wait_count = 0;
|
121
122
|
backend->pending_sqes = 0;
|
122
123
|
|
123
124
|
return self;
|
@@ -149,26 +150,18 @@ static inline bool cq_ring_needs_flush(struct io_uring *ring) {
|
|
149
150
|
return IO_URING_READ_ONCE(*ring->sq.kflags) & IORING_SQ_CQ_OVERFLOW;
|
150
151
|
}
|
151
152
|
|
152
|
-
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) {
|
153
154
|
op_context_t *ctx = io_uring_cqe_get_data(cqe);
|
154
155
|
if (!ctx) return;
|
155
156
|
|
156
157
|
ctx->result = cqe->res;
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
// completion, so we can release the context
|
161
|
-
context_store_release(&backend->store, ctx);
|
162
|
-
else {
|
163
|
-
// otherwise, we mark it as completed, schedule the fiber and let it deal
|
164
|
-
// with releasing the context
|
165
|
-
ctx->completed = 1;
|
166
|
-
if (ctx->result != -ECANCELED) Fiber_make_runnable(ctx->fiber, ctx->resume_value);
|
167
|
-
}
|
158
|
+
if (ctx->ref_count == 2 && ctx->result != -ECANCELED && ctx->fiber)
|
159
|
+
Fiber_make_runnable(ctx->fiber, ctx->resume_value);
|
160
|
+
context_store_release(&backend->store, ctx);
|
168
161
|
}
|
169
162
|
|
170
163
|
// adapted from io_uring_peek_batch_cqe in queue.c
|
171
|
-
// this peeks at cqes and
|
164
|
+
// this peeks at cqes and handles each available cqe
|
172
165
|
void io_uring_backend_handle_ready_cqes(Backend_t *backend) {
|
173
166
|
struct io_uring *ring = &backend->ring;
|
174
167
|
bool overflow_checked = false;
|
@@ -218,16 +211,6 @@ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue
|
|
218
211
|
Backend_t *backend;
|
219
212
|
GetBackend(self, backend);
|
220
213
|
|
221
|
-
if (is_nowait) {
|
222
|
-
backend->poll_no_wait_count++;
|
223
|
-
if (backend->poll_no_wait_count < 10) return self;
|
224
|
-
|
225
|
-
long runnable_count = Runqueue_len(runqueue);
|
226
|
-
if (backend->poll_no_wait_count < runnable_count) return self;
|
227
|
-
}
|
228
|
-
|
229
|
-
backend->poll_no_wait_count = 0;
|
230
|
-
|
231
214
|
if (is_nowait && backend->pending_sqes) {
|
232
215
|
backend->pending_sqes = 0;
|
233
216
|
io_uring_submit(&backend->ring);
|
@@ -282,10 +265,9 @@ int io_uring_backend_defer_submit_and_await(
|
|
282
265
|
|
283
266
|
switchpoint_result = backend_await(backend);
|
284
267
|
|
285
|
-
if (
|
268
|
+
if (ctx->ref_count > 1) {
|
269
|
+
// op was not completed (an exception was raised), so we need to cancel it
|
286
270
|
ctx->result = -ECANCELED;
|
287
|
-
|
288
|
-
// op was not completed, so we need to cancel it
|
289
271
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
290
272
|
io_uring_prep_cancel(sqe, ctx, 0);
|
291
273
|
backend->pending_sqes = 0;
|
@@ -299,7 +281,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
299
281
|
}
|
300
282
|
|
301
283
|
VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
|
302
|
-
op_context_t *ctx =
|
284
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
|
303
285
|
VALUE resumed_value = Qnil;
|
304
286
|
|
305
287
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
@@ -314,8 +296,8 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
314
296
|
Backend_t *backend;
|
315
297
|
rb_io_t *fptr;
|
316
298
|
long dynamic_len = length == Qnil;
|
317
|
-
long
|
318
|
-
int shrinkable = io_setstrbuf(&str,
|
299
|
+
long buffer_size = dynamic_len ? 4096 : NUM2INT(length);
|
300
|
+
int shrinkable = io_setstrbuf(&str, buffer_size);
|
319
301
|
char *buf = RSTRING_PTR(str);
|
320
302
|
long total = 0;
|
321
303
|
int read_to_eof = RTEST(to_eof);
|
@@ -331,14 +313,14 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
331
313
|
|
332
314
|
while (1) {
|
333
315
|
VALUE resume_value = Qnil;
|
334
|
-
op_context_t *ctx =
|
316
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
335
317
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
336
|
-
io_uring_prep_read(sqe, fptr->fd, buf,
|
318
|
+
io_uring_prep_read(sqe, fptr->fd, buf, buffer_size - total, -1);
|
337
319
|
|
338
320
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
339
|
-
|
321
|
+
int completed = context_store_release(&backend->store, ctx);
|
340
322
|
RAISE_IF_EXCEPTION(resume_value);
|
341
|
-
if (!
|
323
|
+
if (!completed) return resume_value;
|
342
324
|
RB_GC_GUARD(resume_value);
|
343
325
|
|
344
326
|
if (result < 0)
|
@@ -349,14 +331,15 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
349
331
|
total += result;
|
350
332
|
if (!read_to_eof) break;
|
351
333
|
|
352
|
-
if (total ==
|
334
|
+
if (total == buffer_size) {
|
353
335
|
if (!dynamic_len) break;
|
354
336
|
|
337
|
+
// resize buffer
|
355
338
|
rb_str_resize(str, total);
|
356
|
-
rb_str_modify_expand(str,
|
339
|
+
rb_str_modify_expand(str, buffer_size);
|
357
340
|
buf = RSTRING_PTR(str) + total;
|
358
341
|
shrinkable = 0;
|
359
|
-
|
342
|
+
buffer_size += buffer_size;
|
360
343
|
}
|
361
344
|
else buf += result;
|
362
345
|
}
|
@@ -391,14 +374,14 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
391
374
|
|
392
375
|
while (1) {
|
393
376
|
VALUE resume_value = Qnil;
|
394
|
-
op_context_t *ctx =
|
377
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
395
378
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
396
379
|
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
397
380
|
|
398
381
|
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
399
|
-
|
382
|
+
int completed = context_store_release(&backend->store, ctx);
|
400
383
|
RAISE_IF_EXCEPTION(resume_value);
|
401
|
-
if (!
|
384
|
+
if (!completed) return resume_value;
|
402
385
|
RB_GC_GUARD(resume_value);
|
403
386
|
|
404
387
|
if (result < 0)
|
@@ -438,14 +421,14 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
438
421
|
|
439
422
|
while (1) {
|
440
423
|
VALUE resume_value = Qnil;
|
441
|
-
op_context_t *ctx =
|
424
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
442
425
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
443
426
|
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
444
427
|
|
445
428
|
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
446
|
-
|
429
|
+
int completed = context_store_release(&backend->store, ctx);
|
447
430
|
RAISE_IF_EXCEPTION(resume_value);
|
448
|
-
if (!
|
431
|
+
if (!completed) return resume_value;
|
449
432
|
RB_GC_GUARD(resume_value);
|
450
433
|
|
451
434
|
if (result < 0)
|
@@ -481,14 +464,14 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
481
464
|
|
482
465
|
while (left > 0) {
|
483
466
|
VALUE resume_value = Qnil;
|
484
|
-
op_context_t *ctx =
|
467
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
|
485
468
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
486
469
|
io_uring_prep_write(sqe, fptr->fd, buf, left, -1);
|
487
470
|
|
488
471
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
489
|
-
|
472
|
+
int completed = context_store_release(&backend->store, ctx);
|
490
473
|
RAISE_IF_EXCEPTION(resume_value);
|
491
|
-
if (!
|
474
|
+
if (!completed) return resume_value;
|
492
475
|
RB_GC_GUARD(resume_value);
|
493
476
|
|
494
477
|
if (result < 0)
|
@@ -530,17 +513,17 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
530
513
|
|
531
514
|
while (1) {
|
532
515
|
VALUE resume_value = Qnil;
|
533
|
-
op_context_t *ctx =
|
516
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
|
534
517
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
535
518
|
io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
|
536
519
|
|
537
520
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
538
|
-
|
521
|
+
int completed = context_store_release(&backend->store, ctx);
|
539
522
|
if (TEST_EXCEPTION(resume_value)) {
|
540
523
|
free(iov);
|
541
524
|
RAISE_EXCEPTION(resume_value);
|
542
525
|
}
|
543
|
-
if (!
|
526
|
+
if (!completed) {
|
544
527
|
free(iov);
|
545
528
|
return resume_value;
|
546
529
|
}
|
@@ -603,14 +586,14 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
|
603
586
|
|
604
587
|
while (1) {
|
605
588
|
VALUE resume_value = Qnil;
|
606
|
-
op_context_t *ctx =
|
589
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
607
590
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
608
591
|
io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
|
609
592
|
|
610
593
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
611
|
-
|
594
|
+
int completed = context_store_release(&backend->store, ctx);
|
612
595
|
RAISE_IF_EXCEPTION(resume_value);
|
613
|
-
if (!
|
596
|
+
if (!completed) return resume_value;
|
614
597
|
RB_GC_GUARD(resume_value);
|
615
598
|
|
616
599
|
if (result < 0)
|
@@ -650,14 +633,14 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
650
633
|
|
651
634
|
while (1) {
|
652
635
|
VALUE resume_value = Qnil;
|
653
|
-
op_context_t *ctx =
|
636
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
654
637
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
655
638
|
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
656
639
|
|
657
640
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
658
|
-
|
641
|
+
int completed = context_store_release(&backend->store, ctx);
|
659
642
|
RAISE_IF_EXCEPTION(resume_value);
|
660
|
-
if (!
|
643
|
+
if (!completed) return resume_value;
|
661
644
|
RB_GC_GUARD(resume_value);
|
662
645
|
|
663
646
|
if (result < 0)
|
@@ -696,14 +679,14 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
696
679
|
|
697
680
|
while (1) {
|
698
681
|
VALUE resume_value = Qnil;
|
699
|
-
op_context_t *ctx =
|
682
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
700
683
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
701
684
|
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
702
685
|
|
703
686
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
704
|
-
|
687
|
+
int completed = context_store_release(&backend->store, ctx);
|
705
688
|
RAISE_IF_EXCEPTION(resume_value);
|
706
|
-
if (!
|
689
|
+
if (!completed) return resume_value;
|
707
690
|
RB_GC_GUARD(resume_value);
|
708
691
|
|
709
692
|
if (result < 0)
|
@@ -739,14 +722,14 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
739
722
|
|
740
723
|
while (left > 0) {
|
741
724
|
VALUE resume_value = Qnil;
|
742
|
-
op_context_t *ctx =
|
725
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
|
743
726
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
744
727
|
io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
|
745
728
|
|
746
729
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
747
|
-
|
730
|
+
int completed = context_store_release(&backend->store, ctx);
|
748
731
|
RAISE_IF_EXCEPTION(resume_value);
|
749
|
-
if (!
|
732
|
+
if (!completed) return resume_value;
|
750
733
|
RB_GC_GUARD(resume_value);
|
751
734
|
|
752
735
|
if (result < 0)
|
@@ -773,14 +756,14 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
773
756
|
|
774
757
|
while (1) {
|
775
758
|
VALUE resume_value = Qnil;
|
776
|
-
op_context_t *ctx =
|
759
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
|
777
760
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
778
761
|
io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
|
779
762
|
|
780
763
|
int fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
781
|
-
|
764
|
+
int completed = context_store_release(&backend->store, ctx);
|
782
765
|
RAISE_IF_EXCEPTION(resume_value);
|
783
|
-
if (!
|
766
|
+
if (!completed) return resume_value;
|
784
767
|
RB_GC_GUARD(resume_value);
|
785
768
|
|
786
769
|
if (fd < 0)
|
@@ -824,6 +807,60 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
824
807
|
return self;
|
825
808
|
}
|
826
809
|
|
810
|
+
VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen, int loop) {
|
811
|
+
rb_io_t *src_fptr;
|
812
|
+
rb_io_t *dest_fptr;
|
813
|
+
VALUE underlying_io;
|
814
|
+
int total = 0;
|
815
|
+
|
816
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
817
|
+
if (underlying_io != Qnil) src = underlying_io;
|
818
|
+
GetOpenFile(src, src_fptr);
|
819
|
+
io_unset_nonblock(src_fptr, src);
|
820
|
+
|
821
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
822
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
823
|
+
dest = rb_io_get_write_io(dest);
|
824
|
+
GetOpenFile(dest, dest_fptr);
|
825
|
+
io_unset_nonblock(dest_fptr, dest);
|
826
|
+
|
827
|
+
VALUE resume_value = Qnil;
|
828
|
+
|
829
|
+
while (1) {
|
830
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
|
831
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
832
|
+
io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
|
833
|
+
|
834
|
+
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
835
|
+
int completed = context_store_release(&backend->store, ctx);
|
836
|
+
RAISE_IF_EXCEPTION(resume_value);
|
837
|
+
if (!completed) return resume_value;
|
838
|
+
|
839
|
+
if (result < 0)
|
840
|
+
rb_syserr_fail(-result, strerror(-result));
|
841
|
+
|
842
|
+
total += result;
|
843
|
+
if (result == 0 || !loop) return INT2NUM(total);
|
844
|
+
}
|
845
|
+
|
846
|
+
RB_GC_GUARD(resume_value);
|
847
|
+
}
|
848
|
+
|
849
|
+
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
850
|
+
Backend_t *backend;
|
851
|
+
GetBackend(self, backend);
|
852
|
+
|
853
|
+
return io_uring_backend_splice(backend, src, dest, maxlen, 0);
|
854
|
+
}
|
855
|
+
|
856
|
+
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
|
857
|
+
Backend_t *backend;
|
858
|
+
GetBackend(self, backend);
|
859
|
+
|
860
|
+
return io_uring_backend_splice(backend, src, dest, chunksize, 1);
|
861
|
+
}
|
862
|
+
|
863
|
+
|
827
864
|
VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
828
865
|
Backend_t *backend;
|
829
866
|
rb_io_t *fptr;
|
@@ -841,13 +878,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
841
878
|
addr.sin_port = htons(NUM2INT(port));
|
842
879
|
|
843
880
|
VALUE resume_value = Qnil;
|
844
|
-
op_context_t *ctx =
|
881
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_CONNECT);
|
845
882
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
846
883
|
io_uring_prep_connect(sqe, fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
|
847
884
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
848
|
-
|
885
|
+
int completed = context_store_release(&backend->store, ctx);
|
849
886
|
RAISE_IF_EXCEPTION(resume_value);
|
850
|
-
if (!
|
887
|
+
if (!completed) return resume_value;
|
851
888
|
RB_GC_GUARD(resume_value);
|
852
889
|
|
853
890
|
if (result < 0) rb_syserr_fail(-result, strerror(-result));
|
@@ -887,12 +924,11 @@ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duratio
|
|
887
924
|
struct __kernel_timespec ts = double_to_timespec(duration);
|
888
925
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
889
926
|
|
890
|
-
op_context_t *ctx =
|
927
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
891
928
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
892
929
|
|
893
930
|
io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
|
894
|
-
|
895
|
-
return ctx->completed;
|
931
|
+
return context_store_release(&backend->store, ctx);
|
896
932
|
}
|
897
933
|
|
898
934
|
VALUE Backend_sleep(VALUE self, VALUE duration) {
|
@@ -940,7 +976,7 @@ struct Backend_timeout_ctx {
|
|
940
976
|
|
941
977
|
VALUE Backend_timeout_ensure(VALUE arg) {
|
942
978
|
struct Backend_timeout_ctx *timeout_ctx = (struct Backend_timeout_ctx *)arg;
|
943
|
-
if (
|
979
|
+
if (timeout_ctx->ctx->ref_count) {
|
944
980
|
timeout_ctx->ctx->result = -ECANCELED;
|
945
981
|
|
946
982
|
// op was not completed, so we need to cancel it
|
@@ -949,7 +985,7 @@ VALUE Backend_timeout_ensure(VALUE arg) {
|
|
949
985
|
timeout_ctx->backend->pending_sqes = 0;
|
950
986
|
io_uring_submit(&timeout_ctx->backend->ring);
|
951
987
|
}
|
952
|
-
|
988
|
+
context_store_release(&timeout_ctx->backend->store, timeout_ctx->ctx);
|
953
989
|
return Qnil;
|
954
990
|
}
|
955
991
|
|
@@ -967,7 +1003,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
967
1003
|
|
968
1004
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
969
1005
|
|
970
|
-
op_context_t *ctx =
|
1006
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
971
1007
|
ctx->resume_value = timeout;
|
972
1008
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
973
1009
|
io_uring_sqe_set_data(sqe, ctx);
|
@@ -1035,6 +1071,130 @@ VALUE Backend_kind(VALUE self) {
|
|
1035
1071
|
return SYM_io_uring;
|
1036
1072
|
}
|
1037
1073
|
|
1074
|
+
struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, VALUE str) {
|
1075
|
+
rb_io_t *fptr;
|
1076
|
+
VALUE underlying_io;
|
1077
|
+
|
1078
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
1079
|
+
if (underlying_io != Qnil) io = underlying_io;
|
1080
|
+
io = rb_io_get_write_io(io);
|
1081
|
+
GetOpenFile(io, fptr);
|
1082
|
+
io_unset_nonblock(fptr, io);
|
1083
|
+
|
1084
|
+
char *buf = StringValuePtr(str);
|
1085
|
+
long len = RSTRING_LEN(str);
|
1086
|
+
|
1087
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
1088
|
+
io_uring_prep_write(sqe, fptr->fd, buf, len, -1);
|
1089
|
+
return sqe;
|
1090
|
+
}
|
1091
|
+
|
1092
|
+
struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VALUE str, VALUE flags) {
|
1093
|
+
rb_io_t *fptr;
|
1094
|
+
VALUE underlying_io;
|
1095
|
+
|
1096
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
1097
|
+
if (underlying_io != Qnil) io = underlying_io;
|
1098
|
+
io = rb_io_get_write_io(io);
|
1099
|
+
GetOpenFile(io, fptr);
|
1100
|
+
io_unset_nonblock(fptr, io);
|
1101
|
+
|
1102
|
+
char *buf = StringValuePtr(str);
|
1103
|
+
long len = RSTRING_LEN(str);
|
1104
|
+
int flags_int = NUM2INT(flags);
|
1105
|
+
|
1106
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
1107
|
+
io_uring_prep_send(sqe, fptr->fd, buf, len, flags_int);
|
1108
|
+
return sqe;
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen) {
|
1112
|
+
rb_io_t *src_fptr;
|
1113
|
+
rb_io_t *dest_fptr;
|
1114
|
+
VALUE underlying_io;
|
1115
|
+
|
1116
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
1117
|
+
if (underlying_io != Qnil) src = underlying_io;
|
1118
|
+
GetOpenFile(src, src_fptr);
|
1119
|
+
io_unset_nonblock(src_fptr, src);
|
1120
|
+
|
1121
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
1122
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
1123
|
+
dest = rb_io_get_write_io(dest);
|
1124
|
+
GetOpenFile(dest, dest_fptr);
|
1125
|
+
io_unset_nonblock(dest_fptr, dest);
|
1126
|
+
|
1127
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
1128
|
+
io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
|
1129
|
+
return sqe;
|
1130
|
+
}
|
1131
|
+
|
1132
|
+
VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
1133
|
+
VALUE resume_value = Qnil;
|
1134
|
+
unsigned int sqe_count = 0;
|
1135
|
+
struct io_uring_sqe *last_sqe = 0;
|
1136
|
+
Backend_t *backend;
|
1137
|
+
GetBackend(self, backend);
|
1138
|
+
if (argc == 0) return resume_value;
|
1139
|
+
|
1140
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_CHAIN);
|
1141
|
+
for (int i = 0; i < argc; i++) {
|
1142
|
+
VALUE op = argv[i];
|
1143
|
+
VALUE op_type = RARRAY_AREF(op, 0);
|
1144
|
+
VALUE op_len = RARRAY_LEN(op);
|
1145
|
+
|
1146
|
+
if (op_type == SYM_write && op_len == 3) {
|
1147
|
+
last_sqe = Backend_chain_prepare_write(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
|
1148
|
+
}
|
1149
|
+
else if (op_type == SYM_send && op_len == 4)
|
1150
|
+
last_sqe = Backend_chain_prepare_send(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
1151
|
+
else if (op_type == SYM_splice && op_len == 4)
|
1152
|
+
last_sqe = Backend_chain_prepare_splice(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
1153
|
+
else {
|
1154
|
+
if (sqe_count) {
|
1155
|
+
io_uring_sqe_set_data(last_sqe, ctx);
|
1156
|
+
io_uring_sqe_set_flags(last_sqe, IOSQE_ASYNC);
|
1157
|
+
|
1158
|
+
ctx->ref_count = sqe_count;
|
1159
|
+
ctx->result = -ECANCELED;
|
1160
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
1161
|
+
io_uring_prep_cancel(sqe, ctx, 0);
|
1162
|
+
backend->pending_sqes = 0;
|
1163
|
+
io_uring_submit(&backend->ring);
|
1164
|
+
}
|
1165
|
+
else {
|
1166
|
+
ctx->ref_count = 1;
|
1167
|
+
context_store_release(&backend->store, ctx);
|
1168
|
+
}
|
1169
|
+
rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
io_uring_sqe_set_data(last_sqe, ctx);
|
1173
|
+
unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC & IOSQE_IO_LINK;
|
1174
|
+
io_uring_sqe_set_flags(last_sqe, flags);
|
1175
|
+
sqe_count++;
|
1176
|
+
}
|
1177
|
+
|
1178
|
+
ctx->ref_count = sqe_count + 1;
|
1179
|
+
io_uring_backend_defer_submit(backend);
|
1180
|
+
resume_value = backend_await(backend);
|
1181
|
+
int result = ctx->result;
|
1182
|
+
int completed = context_store_release(&backend->store, ctx);
|
1183
|
+
if (!completed) {
|
1184
|
+
// op was not completed (an exception was raised), so we need to cancel it
|
1185
|
+
ctx->result = -ECANCELED;
|
1186
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
1187
|
+
io_uring_prep_cancel(sqe, ctx, 0);
|
1188
|
+
backend->pending_sqes = 0;
|
1189
|
+
io_uring_submit(&backend->ring);
|
1190
|
+
RAISE_IF_EXCEPTION(resume_value);
|
1191
|
+
return resume_value;
|
1192
|
+
}
|
1193
|
+
|
1194
|
+
RB_GC_GUARD(resume_value);
|
1195
|
+
return INT2NUM(result);
|
1196
|
+
}
|
1197
|
+
|
1038
1198
|
void Init_Backend() {
|
1039
1199
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
1040
1200
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
@@ -1045,33 +1205,39 @@ void Init_Backend() {
|
|
1045
1205
|
|
1046
1206
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
1047
1207
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1208
|
+
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1209
|
+
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
1048
1210
|
|
1211
|
+
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1212
|
+
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
1213
|
+
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
1214
|
+
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
1049
1215
|
rb_define_method(cBackend, "read", Backend_read, 4);
|
1050
1216
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
1051
|
-
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
1052
|
-
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1053
1217
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
1054
|
-
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
1055
1218
|
rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
|
1219
|
+
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
1056
1220
|
rb_define_method(cBackend, "send", Backend_send, 3);
|
1057
1221
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1058
|
-
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1059
|
-
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
1060
|
-
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
1061
|
-
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
1062
1222
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1063
|
-
rb_define_method(cBackend, "
|
1223
|
+
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1224
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1064
1225
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1065
|
-
rb_define_method(cBackend, "
|
1226
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
1066
1227
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
1228
|
+
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
1229
|
+
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
1230
|
+
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1067
1231
|
|
1068
|
-
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1069
1232
|
|
1070
1233
|
#ifdef POLYPHONY_UNSET_NONBLOCK
|
1071
1234
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1072
1235
|
#endif
|
1073
1236
|
|
1074
1237
|
SYM_io_uring = ID2SYM(rb_intern("io_uring"));
|
1238
|
+
SYM_send = ID2SYM(rb_intern("send"));
|
1239
|
+
SYM_splice = ID2SYM(rb_intern("splice"));
|
1240
|
+
SYM_write = ID2SYM(rb_intern("write"));
|
1075
1241
|
}
|
1076
1242
|
|
1077
1243
|
#endif // POLYPHONY_BACKEND_LIBURING
|