uringmachine 0.29.2 → 0.30.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/CHANGELOG.md +6 -0
- data/TODO.md +24 -85
- data/benchmark/bm_io_ssl.rb +128 -0
- data/benchmark/bm_redis_client.rb +76 -0
- data/benchmark/common.rb +1 -0
- data/benchmark/http_parse.rb +3 -3
- data/ext/um/um.c +51 -0
- data/ext/um/um.h +11 -5
- data/ext/um/um_buffer_pool.c +11 -11
- data/ext/um/um_class.c +57 -0
- data/ext/um/um_ssl.c +6 -5
- data/ext/um/um_stream.c +33 -1
- data/ext/um/um_stream_class.c +14 -0
- data/grant-2025/final-report.md +267 -0
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +41 -0
- data/test/test_ssl.rb +27 -0
- data/test/test_stream.rb +33 -0
- data/test/test_um.rb +157 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a97099f1d89b2333b8be056c91acabad63fad122ce4c802a666e4abb10aca93b
|
|
4
|
+
data.tar.gz: '095878ee7df374be5dc87b74ea636b3def2b611006c76c7d86ae7c00ad064f47'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 339349fd4116011334517f124201a1cf425762c1bafe33ba61fa3eaf175fa5064512f780f8354f8fc317515bab6c4914cb41fba2022d23c17aac7e08d18cad7b
|
|
7
|
+
data.tar.gz: 02c7b30e4143f07382f2788a9f242e6f051f57b438fc3ecc63153a13f318bbbab69e5239add2af31e277c68b21483be60bd542863c8f7aa358d7fa1b0c0793e9
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
|
@@ -1,67 +1,10 @@
|
|
|
1
1
|
## immediate
|
|
2
2
|
|
|
3
|
-
- Add support for returning a value on timeout
|
|
4
|
-
|
|
5
|
-
Since to do this safely we need to actually raise an exception that wraps the
|
|
6
|
-
value, rescue it and return the value, we might want a separate method that
|
|
7
|
-
wraps `#timeout`:
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
TimeoutValueError < StandardError
|
|
11
|
-
|
|
12
|
-
def timeout_with_value(interval, value, &block)
|
|
13
|
-
timeout_error = TimeoutValueError
|
|
14
|
-
timeout(interval, timeout_error, &block)
|
|
15
|
-
rescue TimeoutValueError => e
|
|
16
|
-
raise if e != timeout_error
|
|
17
|
-
|
|
18
|
-
value
|
|
19
|
-
end
|
|
20
|
-
```
|
|
21
|
-
|
|
22
3
|
- Add tests for support for Set in `machine#await`
|
|
23
4
|
- Add tests for support for Set, Array in `machine#join`
|
|
24
5
|
- Add `#read_file` for reading entire file
|
|
25
6
|
- Add `#write_file` for writing entire file
|
|
26
7
|
|
|
27
|
-
- (?) Fix all futex value (Queue, Mutex) to be properly aligned
|
|
28
|
-
|
|
29
|
-
<<<<<<< HEAD
|
|
30
|
-
=======
|
|
31
|
-
## Buffer rings - automatic management
|
|
32
|
-
|
|
33
|
-
- Take the buffer_pool branch, rewrite it
|
|
34
|
-
- Allow multiple stream modes:
|
|
35
|
-
- :buffer_pool - uses buffer rings
|
|
36
|
-
- :ssl - read from an SSL connection (`SSLSocket`)
|
|
37
|
-
- :io - read from an `IO`
|
|
38
|
-
|
|
39
|
-
The API will look something like:
|
|
40
|
-
|
|
41
|
-
```ruby
|
|
42
|
-
# The mode is selected automatically according to the given target
|
|
43
|
-
|
|
44
|
-
stream = UM::Stream.new(machine, fd) # buffer_pool mode (read)
|
|
45
|
-
stream = UM::Stream.new(machine, fd, :recv) # buffer_pool mode (recv)
|
|
46
|
-
stream = UM::Stream.new(machine, ssl_sock) # SSLSocket mode
|
|
47
|
-
stream = UM::Stream.new(machine, conn) # IO mode
|
|
48
|
-
stream = UM::Stream.new(machine, str) # string mode
|
|
49
|
-
stream = UM::Stream.new(machine, io_buf) # IO:Buffer mode
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
This can be very useful in testing of stuff such as protocol implementations:
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
stream = UM::Stream.new(machine, "GET /foo HTTP/1.1\r\nHost: bar.com\r\n")
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
So basically the stream is tied to a machine, and that means it can only be used
|
|
59
|
-
on the thread with which the machine is associated. It is not thread-safe. (This
|
|
60
|
-
is incidentally true also for most of the UringMachine instance methods!)
|
|
61
|
-
|
|
62
|
-
Continued discussion in docs/design/buffer_pool.md
|
|
63
|
-
|
|
64
|
-
>>>>>>> 04d9eb7 (Docs)
|
|
65
8
|
## Balancing I/O with the runqueue
|
|
66
9
|
|
|
67
10
|
- in some cases where there are many entries in the runqueue, this can
|
|
@@ -89,6 +32,8 @@ Continued discussion in docs/design/buffer_pool.md
|
|
|
89
32
|
debouncer = machine.debounce { }
|
|
90
33
|
```
|
|
91
34
|
|
|
35
|
+
- happy eyeballs algo for TCP connect
|
|
36
|
+
|
|
92
37
|
- read multiple files
|
|
93
38
|
|
|
94
39
|
```ruby
|
|
@@ -99,12 +44,31 @@ Continued discussion in docs/design/buffer_pool.md
|
|
|
99
44
|
machine.read_files(*fns) #=> { fn1:, fn2:, fn3:, ...}
|
|
100
45
|
```
|
|
101
46
|
|
|
47
|
+
- more generally, a DSL for expressing batch operations:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
result = machine.batch do |b|
|
|
51
|
+
fns.each { b[it] = read_file(b, it) }
|
|
52
|
+
end
|
|
53
|
+
#=> { fn1 => data1, fn2 => data2, ... }
|
|
54
|
+
|
|
55
|
+
# we can also imagine performing operations in sequence using linking:
|
|
56
|
+
result = machine.batch {
|
|
57
|
+
m.
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
102
63
|
## polyvalent select
|
|
103
64
|
|
|
104
65
|
- select on multiple queues (ala Go)
|
|
105
66
|
- select on mixture of queues and fds
|
|
67
|
+
- select on fibers:
|
|
68
|
+
- select fibers that are done
|
|
69
|
+
- select first done fiber
|
|
106
70
|
|
|
107
|
-
## ops
|
|
71
|
+
## ops still not implemented
|
|
108
72
|
|
|
109
73
|
- splice / - tee
|
|
110
74
|
- sendto
|
|
@@ -123,31 +87,7 @@ Continued discussion in docs/design/buffer_pool.md
|
|
|
123
87
|
When doing a `call`, we need to provide a mailbox for the response. can this be
|
|
124
88
|
automatic?
|
|
125
89
|
|
|
126
|
-
##
|
|
127
|
-
|
|
128
|
-
We're still missing:
|
|
129
|
-
|
|
130
|
-
- limit on line length in `get_line`
|
|
131
|
-
- ability to supply buffer to `get_line` and `get_string`
|
|
132
|
-
- allow read to eof, maybe with `read_to_eof`
|
|
133
|
-
|
|
134
|
-
For the sake of performance, simplicity and explicitness, we change the API as
|
|
135
|
-
follows:
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
stream.get_line(buf, limit)
|
|
139
|
-
# the defaults:
|
|
140
|
-
stream.get_line(nil, -1)
|
|
141
|
-
|
|
142
|
-
stream.get_string(len, buf)
|
|
143
|
-
# defaults:
|
|
144
|
-
stream.get_string(len, nil)
|
|
145
|
-
|
|
146
|
-
# and
|
|
147
|
-
stream.read_to_eof(buf)
|
|
148
|
-
# defaults:
|
|
149
|
-
stream.read_to_eof(nil)
|
|
150
|
-
```
|
|
90
|
+
##
|
|
151
91
|
|
|
152
92
|
## Syntax / pattern for launching/supervising multiple operations
|
|
153
93
|
|
|
@@ -166,6 +106,5 @@ machine.shift_select(*queues) #=> [result, queue]
|
|
|
166
106
|
```ruby
|
|
167
107
|
# addrs: [['1.1.1.1', 80], ['2.2.2.2', 80]]
|
|
168
108
|
# ['1.1.1.1:80', '2.2.2.2:80']
|
|
169
|
-
|
|
109
|
+
tcp_connect_he(*addrs)
|
|
170
110
|
```
|
|
171
|
-
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './common'
|
|
4
|
+
require 'socket'
|
|
5
|
+
require 'openssl'
|
|
6
|
+
require 'localhost/authority'
|
|
7
|
+
|
|
8
|
+
GROUPS = 48
|
|
9
|
+
ITERATIONS = 5000
|
|
10
|
+
|
|
11
|
+
SIZE = 1 << 14
|
|
12
|
+
DATA = '*' * SIZE
|
|
13
|
+
|
|
14
|
+
class UMBenchmark
|
|
15
|
+
def server_ctx
|
|
16
|
+
@server_ctx ||= Localhost::Authority.fetch.server_context
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ssl_wrap(sock, ctx)
|
|
20
|
+
OpenSSL::SSL::SSLSocket.new(sock, ctx).tap { it.sync_close = true }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ssl_socketpair(machine)
|
|
24
|
+
sock1, sock2 = Socket.socketpair(:AF_UNIX, :SOCK_STREAM, 0)
|
|
25
|
+
ssl1 = ssl_wrap(sock1, server_ctx)
|
|
26
|
+
ssl2 = ssl_wrap(sock2, OpenSSL::SSL::SSLContext.new)
|
|
27
|
+
|
|
28
|
+
if !machine
|
|
29
|
+
t = Thread.new { ssl1.accept rescue nil }
|
|
30
|
+
ssl2.connect
|
|
31
|
+
t.join
|
|
32
|
+
else
|
|
33
|
+
machine.ssl_set_bio(ssl1)
|
|
34
|
+
machine.ssl_set_bio(ssl2)
|
|
35
|
+
f = machine.spin { ssl1.accept rescue nil }
|
|
36
|
+
ssl2.connect
|
|
37
|
+
machine.join(f)
|
|
38
|
+
end
|
|
39
|
+
[ssl1, ssl2]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def do_threads(threads, ios)
|
|
43
|
+
GROUPS.times do
|
|
44
|
+
r, w = ssl_socketpair(nil)
|
|
45
|
+
threads << Thread.new do
|
|
46
|
+
ITERATIONS.times { w.write(DATA) }
|
|
47
|
+
w.close
|
|
48
|
+
end
|
|
49
|
+
threads << Thread.new do
|
|
50
|
+
ITERATIONS.times { r.readpartial(SIZE) }
|
|
51
|
+
r.close
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def do_thread_pool(thread_pool, ios)
|
|
57
|
+
GROUPS.times do
|
|
58
|
+
r, w = ssl_socketpair(nil)
|
|
59
|
+
r.sync = true
|
|
60
|
+
w.sync = true
|
|
61
|
+
ios << r << w
|
|
62
|
+
ITERATIONS.times {
|
|
63
|
+
thread_pool.queue { w.write(DATA) }
|
|
64
|
+
thread_pool.queue { r.readpartial(SIZE) }
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def do_scheduler(scheduler, ios)
|
|
70
|
+
GROUPS.times do
|
|
71
|
+
r, w = ssl_socketpair(nil)
|
|
72
|
+
r.sync = true
|
|
73
|
+
w.sync = true
|
|
74
|
+
Fiber.schedule do
|
|
75
|
+
ITERATIONS.times { w.write(DATA) }
|
|
76
|
+
w.close
|
|
77
|
+
end
|
|
78
|
+
Fiber.schedule do
|
|
79
|
+
ITERATIONS.times { r.readpartial(SIZE) }
|
|
80
|
+
r.close
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def do_scheduler_x(div, scheduler, ios)
|
|
86
|
+
(GROUPS/div).times do
|
|
87
|
+
r, w = ssl_socketpair(nil)
|
|
88
|
+
r.sync = true
|
|
89
|
+
w.sync = true
|
|
90
|
+
Fiber.schedule do
|
|
91
|
+
ITERATIONS.times { w.write(DATA) }
|
|
92
|
+
w.close
|
|
93
|
+
end
|
|
94
|
+
Fiber.schedule do
|
|
95
|
+
ITERATIONS.times { r.readpartial(SIZE) }
|
|
96
|
+
r.close
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def do_um(machine, fibers, fds)
|
|
102
|
+
GROUPS.times do
|
|
103
|
+
r, w = ssl_socketpair(machine)
|
|
104
|
+
fibers << machine.spin do
|
|
105
|
+
ITERATIONS.times { machine.ssl_write(w, DATA, SIZE) }
|
|
106
|
+
machine.close_async(w)
|
|
107
|
+
end
|
|
108
|
+
fibers << machine.spin do
|
|
109
|
+
ITERATIONS.times { machine.ssl_read(r, +'', SIZE) }
|
|
110
|
+
machine.close_async(r)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def do_um_x(div, machine, fibers, fds)
|
|
116
|
+
(GROUPS/div).times do
|
|
117
|
+
r, w = ssl_socketpair(machine)
|
|
118
|
+
fibers << machine.spin do
|
|
119
|
+
ITERATIONS.times { machine.ssl_write(w, DATA, SIZE) }
|
|
120
|
+
machine.close_async(w)
|
|
121
|
+
end
|
|
122
|
+
fibers << machine.spin do
|
|
123
|
+
ITERATIONS.times { machine.ssl_read(r, +'', SIZE) }
|
|
124
|
+
machine.close_async(r)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './common'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
|
|
6
|
+
C = ENV['C']&.to_i || 50
|
|
7
|
+
I = 10
|
|
8
|
+
puts "C=#{C}"
|
|
9
|
+
|
|
10
|
+
class UMBenchmark
|
|
11
|
+
CONTAINER_NAME = "redis-#{SecureRandom.hex}"
|
|
12
|
+
|
|
13
|
+
def start_redis_server
|
|
14
|
+
`docker run --name #{CONTAINER_NAME} -d -p 6379:6379 redis:latest`
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stop_redis_server
|
|
18
|
+
`docker stop #{CONTAINER_NAME}`
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_redis_conn(retries = 0)
|
|
22
|
+
Redis.new
|
|
23
|
+
rescue
|
|
24
|
+
if retries < 3
|
|
25
|
+
sleep 0.5
|
|
26
|
+
create_redis_conn(retries + 1)
|
|
27
|
+
else
|
|
28
|
+
raise
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def query_redis(conn)
|
|
33
|
+
conn.set('abc', 'def')
|
|
34
|
+
p conn.get('abc')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def with_container
|
|
38
|
+
start_redis_server
|
|
39
|
+
sleep 0.5
|
|
40
|
+
yield
|
|
41
|
+
rescue Exception => e
|
|
42
|
+
p e
|
|
43
|
+
p e.backtrace
|
|
44
|
+
ensure
|
|
45
|
+
stop_redis_server
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def benchmark
|
|
49
|
+
with_container {
|
|
50
|
+
Benchmark.bm { run_benchmarks(it) }
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# def do_threads(threads, ios)
|
|
55
|
+
# C.times.map do
|
|
56
|
+
# threads << Thread.new do
|
|
57
|
+
# conn = create_redis_conn
|
|
58
|
+
# I.times { query_redis(conn) }
|
|
59
|
+
# ensure
|
|
60
|
+
# conn.close
|
|
61
|
+
# end
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
|
|
65
|
+
def do_scheduler(scheduler, ios)
|
|
66
|
+
return if !scheduler.is_a?(UM::FiberScheduler)
|
|
67
|
+
C.times do
|
|
68
|
+
Fiber.schedule do
|
|
69
|
+
conn = create_redis_conn
|
|
70
|
+
I.times { query_redis(conn) }
|
|
71
|
+
ensure
|
|
72
|
+
conn.close
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/benchmark/common.rb
CHANGED
data/benchmark/http_parse.rb
CHANGED
|
@@ -62,7 +62,7 @@ end
|
|
|
62
62
|
|
|
63
63
|
require 'stringio'
|
|
64
64
|
|
|
65
|
-
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/
|
|
65
|
+
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/1\.1)/i
|
|
66
66
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
67
67
|
|
|
68
68
|
def get_line(fd, sio, buffer)
|
|
@@ -137,7 +137,7 @@ def stream_parse_headers(fd)
|
|
|
137
137
|
return nil if !headers
|
|
138
138
|
|
|
139
139
|
while true
|
|
140
|
-
line = stream.get_line(
|
|
140
|
+
line = stream.get_line(0)
|
|
141
141
|
break if line.empty?
|
|
142
142
|
|
|
143
143
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -150,7 +150,7 @@ def stream_parse_headers(fd)
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
def stream_get_request_line(stream, buf)
|
|
153
|
-
line = stream.get_line(
|
|
153
|
+
line = stream.get_line(0)
|
|
154
154
|
|
|
155
155
|
m = line.match(RE_REQUEST_LINE)
|
|
156
156
|
return nil if !m
|
data/ext/um/um.c
CHANGED
|
@@ -1285,6 +1285,57 @@ VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned in
|
|
|
1285
1285
|
return statx_to_hash(&stat);
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
|
+
VALUE um_splice(struct um *machine, int in_fd, int out_fd, uint nbytes) {
|
|
1289
|
+
struct um_op *op = um_op_acquire(machine);
|
|
1290
|
+
um_prep_op(machine, op, OP_SPLICE, 2, 0);
|
|
1291
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
1292
|
+
io_uring_prep_splice(sqe, in_fd, -1, out_fd, -1, nbytes, 0);
|
|
1293
|
+
|
|
1294
|
+
VALUE ret = um_yield(machine);
|
|
1295
|
+
|
|
1296
|
+
if (likely(um_verify_op_completion(machine, op, false))) ret = INT2NUM(op->result.res);
|
|
1297
|
+
um_op_release(machine, op);
|
|
1298
|
+
|
|
1299
|
+
RAISE_IF_EXCEPTION(ret);
|
|
1300
|
+
RB_GC_GUARD(ret);
|
|
1301
|
+
|
|
1302
|
+
return ret;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
VALUE um_tee(struct um *machine, int in_fd, int out_fd, uint nbytes) {
|
|
1306
|
+
struct um_op *op = um_op_acquire(machine);
|
|
1307
|
+
um_prep_op(machine, op, OP_TEE, 2, 0);
|
|
1308
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
1309
|
+
io_uring_prep_tee(sqe, in_fd, out_fd, nbytes, 0);
|
|
1310
|
+
|
|
1311
|
+
VALUE ret = um_yield(machine);
|
|
1312
|
+
|
|
1313
|
+
if (likely(um_verify_op_completion(machine, op, false))) ret = INT2NUM(op->result.res);
|
|
1314
|
+
um_op_release(machine, op);
|
|
1315
|
+
|
|
1316
|
+
RAISE_IF_EXCEPTION(ret);
|
|
1317
|
+
RB_GC_GUARD(ret);
|
|
1318
|
+
|
|
1319
|
+
return ret;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
VALUE um_fsync(struct um *machine, int fd) {
|
|
1323
|
+
struct um_op *op = um_op_acquire(machine);
|
|
1324
|
+
um_prep_op(machine, op, OP_FSYNC, 2, 0);
|
|
1325
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
1326
|
+
io_uring_prep_fsync(sqe, fd, 0);
|
|
1327
|
+
|
|
1328
|
+
VALUE ret = um_yield(machine);
|
|
1329
|
+
|
|
1330
|
+
if (likely(um_verify_op_completion(machine, op, false))) ret = INT2NUM(op->result.res);
|
|
1331
|
+
um_op_release(machine, op);
|
|
1332
|
+
|
|
1333
|
+
RAISE_IF_EXCEPTION(ret);
|
|
1334
|
+
RB_GC_GUARD(ret);
|
|
1335
|
+
|
|
1336
|
+
return ret;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1288
1339
|
/*******************************************************************************
|
|
1289
1340
|
multishot ops
|
|
1290
1341
|
*******************************************************************************/
|
data/ext/um/um.h
CHANGED
|
@@ -48,6 +48,9 @@ enum um_op_kind {
|
|
|
48
48
|
OP_CLOSE,
|
|
49
49
|
OP_CLOSE_ASYNC,
|
|
50
50
|
OP_STATX,
|
|
51
|
+
OP_SPLICE,
|
|
52
|
+
OP_TEE,
|
|
53
|
+
OP_FSYNC,
|
|
51
54
|
|
|
52
55
|
OP_ACCEPT,
|
|
53
56
|
OP_RECV,
|
|
@@ -161,7 +164,7 @@ struct um_op {
|
|
|
161
164
|
struct iovec *iovecs; // used for vectorized write/send
|
|
162
165
|
siginfo_t siginfo; // used for waitid
|
|
163
166
|
int int_value; // used for getsockopt
|
|
164
|
-
size_t bp_commit_level; // buffer pool commit
|
|
167
|
+
size_t bp_commit_level; // buffer pool commit level
|
|
165
168
|
};
|
|
166
169
|
};
|
|
167
170
|
|
|
@@ -325,7 +328,6 @@ void um_op_list_compact(struct um *machine, struct um_op *head);
|
|
|
325
328
|
void um_op_multishot_results_push(struct um *machine, struct um_op *op, __s32 res, __u32 flags);
|
|
326
329
|
void um_op_multishot_results_clear(struct um *machine, struct um_op *op);
|
|
327
330
|
|
|
328
|
-
struct um_segment *um_segment_alloc(struct um *machine);
|
|
329
331
|
void um_segment_free(struct um *machine, struct um_segment *segment);
|
|
330
332
|
|
|
331
333
|
void um_runqueue_push(struct um *machine, struct um_op *op);
|
|
@@ -391,6 +393,9 @@ VALUE um_waitid_status(struct um *machine, int idtype, int id, int options);
|
|
|
391
393
|
#endif
|
|
392
394
|
|
|
393
395
|
VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask);
|
|
396
|
+
VALUE um_splice(struct um *machine, int in_fd, int out_fd, uint nbytes);
|
|
397
|
+
VALUE um_tee(struct um *machine, int in_fd, int out_fd, uint nbytes);
|
|
398
|
+
VALUE um_fsync(struct um *machine, int fd);
|
|
394
399
|
|
|
395
400
|
VALUE um_accept(struct um *machine, int fd);
|
|
396
401
|
VALUE um_accept_each(struct um *machine, int fd);
|
|
@@ -437,6 +442,7 @@ void stream_clear(struct um_stream *stream);
|
|
|
437
442
|
VALUE stream_get_line(struct um_stream *stream, VALUE buf, size_t maxlen);
|
|
438
443
|
VALUE stream_get_string(struct um_stream *stream, VALUE out_buffer, ssize_t len, size_t inc, int safe_inc);
|
|
439
444
|
void stream_skip(struct um_stream *stream, size_t inc, int safe_inc);
|
|
445
|
+
void stream_each(struct um_stream *stream);
|
|
440
446
|
VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
|
|
441
447
|
void resp_encode(struct um_write_buffer *buf, VALUE obj);
|
|
442
448
|
void resp_encode_cmd(struct um_write_buffer *buf, int argc, VALUE *argv);
|
|
@@ -454,9 +460,9 @@ void um_sidecar_signal_wait(struct um *machine);
|
|
|
454
460
|
void um_sidecar_signal_wake(struct um *machine);
|
|
455
461
|
|
|
456
462
|
void um_ssl_set_bio(struct um *machine, VALUE ssl_obj);
|
|
457
|
-
int um_ssl_read(struct um *machine, VALUE ssl, VALUE buf,
|
|
458
|
-
int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr,
|
|
459
|
-
int um_ssl_write(struct um *machine, VALUE ssl, VALUE buf,
|
|
463
|
+
int um_ssl_read(struct um *machine, VALUE ssl, VALUE buf, size_t maxlen);
|
|
464
|
+
int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr, size_t maxlen);
|
|
465
|
+
int um_ssl_write(struct um *machine, VALUE ssl, VALUE buf, size_t len);
|
|
460
466
|
|
|
461
467
|
void bp_setup(struct um *machine);
|
|
462
468
|
void bp_teardown(struct um *machine);
|
data/ext/um/um_buffer_pool.c
CHANGED
|
@@ -18,7 +18,7 @@ inline struct um_buffer *buffer_alloc(struct um *machine) {
|
|
|
18
18
|
return buffer;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
struct um_buffer *bp_buffer_checkout(struct um *machine) {
|
|
22
22
|
struct um_buffer *buffer = machine->bp_buffer_freelist;
|
|
23
23
|
if (buffer) {
|
|
24
24
|
struct um_buffer *next = buffer->next;
|
|
@@ -50,13 +50,13 @@ inline void buffer_free(struct um *machine, struct um_buffer *buffer) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
void bp_buffer_checkin(struct um *machine, struct um_buffer *buffer) {
|
|
54
54
|
assert(buffer->ref_count > 0);
|
|
55
55
|
buffer->ref_count--;
|
|
56
56
|
if (!buffer->ref_count) buffer_free(machine, buffer);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
void bp_discard_buffer_freelist(struct um *machine) {
|
|
60
60
|
while (machine->bp_buffer_freelist) {
|
|
61
61
|
struct um_buffer *buffer = machine->bp_buffer_freelist;
|
|
62
62
|
struct um_buffer *next = buffer->next;
|
|
@@ -69,7 +69,7 @@ inline void bp_discard_buffer_freelist(struct um *machine) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
void bp_setup(struct um *machine) {
|
|
73
73
|
int ret;
|
|
74
74
|
machine->bp_br = io_uring_setup_buf_ring(&machine->ring, BP_BR_ENTRIES, BP_BGID, IOU_PBUF_RING_INC, &ret);
|
|
75
75
|
if (unlikely(!machine->bp_br)) rb_syserr_fail(ret, strerror(ret));
|
|
@@ -85,7 +85,7 @@ inline void bp_setup(struct um *machine) {
|
|
|
85
85
|
machine->bp_total_commited = 0;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
void bp_teardown(struct um *machine) {
|
|
89
89
|
bp_discard_buffer_freelist(machine);
|
|
90
90
|
for (int i = 0; i < BP_BR_ENTRIES; i++) {
|
|
91
91
|
struct um_buffer *buffer = machine->bp_commited_buffers[i];
|
|
@@ -162,7 +162,7 @@ inline int should_commit_more_p(struct um *machine) {
|
|
|
162
162
|
(machine->bp_total_commited < machine->bp_commit_level);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
void bp_ensure_commit_level(struct um *machine) {
|
|
166
166
|
if (machine->bp_total_commited > (machine->bp_commit_level / 2))
|
|
167
167
|
return;
|
|
168
168
|
|
|
@@ -179,7 +179,7 @@ inline void bp_ensure_commit_level(struct um *machine) {
|
|
|
179
179
|
// size.
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
void bp_handle_enobufs(struct um *machine) {
|
|
183
183
|
if (unlikely(machine->bp_commit_level >= BP_MAX_COMMIT_LEVEL))
|
|
184
184
|
rb_raise(eUMError, "Buffer starvation");
|
|
185
185
|
|
|
@@ -189,7 +189,7 @@ inline void bp_handle_enobufs(struct um *machine) {
|
|
|
189
189
|
bp_discard_buffer_freelist(machine);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
inline struct um_segment *
|
|
192
|
+
inline struct um_segment *segment_alloc(struct um *machine) {
|
|
193
193
|
if (machine->segment_freelist) {
|
|
194
194
|
struct um_segment *segment = machine->segment_freelist;
|
|
195
195
|
machine->segment_freelist = segment->next;
|
|
@@ -208,14 +208,14 @@ inline struct um_segment *um_segment_alloc(struct um *machine) {
|
|
|
208
208
|
return batch;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
void um_segment_free(struct um *machine, struct um_segment *segment) {
|
|
212
212
|
segment->next = machine->segment_freelist;
|
|
213
213
|
machine->segment_freelist = segment;
|
|
214
214
|
machine->metrics.segments_free++;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
struct um_segment *segment =
|
|
217
|
+
struct um_segment *bp_buffer_consume(struct um *machine, struct um_buffer *buffer, size_t len) {
|
|
218
|
+
struct um_segment *segment = segment_alloc(machine);
|
|
219
219
|
segment->ptr = buffer->buf + buffer->pos;
|
|
220
220
|
segment->len = len;
|
|
221
221
|
segment->buffer = buffer;
|
data/ext/um/um_class.c
CHANGED
|
@@ -534,6 +534,60 @@ VALUE UM_statx(VALUE self, VALUE dirfd, VALUE path, VALUE flags, VALUE mask) {
|
|
|
534
534
|
return um_statx(machine, NUM2INT(dirfd), path, NUM2INT(flags), NUM2UINT(mask));
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
/* call-seq:
|
|
538
|
+
* machine.splice(in_fd, out_fd, nbytes) -> len
|
|
539
|
+
*
|
|
540
|
+
* Splices bytes from in_fd to out_fd. At least one of the given fds must be a
|
|
541
|
+
* pipe.
|
|
542
|
+
*
|
|
543
|
+
* - https://www.man7.org/linux/man-pages/man2/splice.2.html
|
|
544
|
+
* - https://www.man7.org/linux/man-pages/man3/io_uring_prep_splice.3.html
|
|
545
|
+
*
|
|
546
|
+
* @param in_fd [Integer] fd to splice from
|
|
547
|
+
* @param out_fd [Integer] fd to splice to
|
|
548
|
+
* @param nbytes [Integer] number of bytes to splice
|
|
549
|
+
* @return [Integer] number of bytes spliced
|
|
550
|
+
*/
|
|
551
|
+
VALUE UM_splice(VALUE self, VALUE in_fd, VALUE out_fd, VALUE nbytes) {
|
|
552
|
+
struct um *machine = um_get_machine(self);
|
|
553
|
+
return um_splice(machine, NUM2INT(in_fd), NUM2INT(out_fd), NUM2UINT(nbytes));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* call-seq:
|
|
557
|
+
* machine.tee(in_fd, out_fd, nbytes) -> len
|
|
558
|
+
*
|
|
559
|
+
* Duplicates bytes from in_fd to out_fd. At least one of the given fds must be
|
|
560
|
+
* a pipe.
|
|
561
|
+
*
|
|
562
|
+
* - https://www.man7.org/linux/man-pages/man2/tee.2.html
|
|
563
|
+
* - https://www.man7.org/linux/man-pages/man3/io_uring_prep_tee.3.html
|
|
564
|
+
*
|
|
565
|
+
* @param in_fd [Integer] fd to copy from
|
|
566
|
+
* @param out_fd [Integer] fd to copy to
|
|
567
|
+
* @param nbytes [Integer] number of bytes to duplicate
|
|
568
|
+
* @return [Integer] number of bytes duplicated
|
|
569
|
+
*/
|
|
570
|
+
VALUE UM_tee(VALUE self, VALUE in_fd, VALUE out_fd, VALUE nbytes) {
|
|
571
|
+
struct um *machine = um_get_machine(self);
|
|
572
|
+
return um_tee(machine, NUM2INT(in_fd), NUM2INT(out_fd), NUM2UINT(nbytes));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* call-seq:
|
|
576
|
+
* machine.fsync(fd) -> 0
|
|
577
|
+
*
|
|
578
|
+
* Flushes all modified file data to the storage device.
|
|
579
|
+
*
|
|
580
|
+
* - https://www.man7.org/linux/man-pages/man2/fsync.2.html
|
|
581
|
+
* - https://www.man7.org/linux/man-pages/man3/io_uring_prep_fsync.3.html
|
|
582
|
+
*
|
|
583
|
+
* @param fd [Integer] fd
|
|
584
|
+
* @return [Integer] 0 if successful
|
|
585
|
+
*/
|
|
586
|
+
VALUE UM_fsync(VALUE self, VALUE fd) {
|
|
587
|
+
struct um *machine = um_get_machine(self);
|
|
588
|
+
return um_fsync(machine, NUM2INT(fd));
|
|
589
|
+
}
|
|
590
|
+
|
|
537
591
|
/* call-seq:
|
|
538
592
|
* machine.close(fd) -> 0
|
|
539
593
|
*
|
|
@@ -1480,6 +1534,9 @@ void Init_UM(void) {
|
|
|
1480
1534
|
rb_define_method(cUM, "writev", UM_writev, -1);
|
|
1481
1535
|
rb_define_method(cUM, "write_async", UM_write_async, -1);
|
|
1482
1536
|
rb_define_method(cUM, "statx", UM_statx, 4);
|
|
1537
|
+
rb_define_method(cUM, "splice", UM_splice, 3);
|
|
1538
|
+
rb_define_method(cUM, "tee", UM_tee, 3);
|
|
1539
|
+
rb_define_method(cUM, "fsync", UM_fsync, 1);
|
|
1483
1540
|
|
|
1484
1541
|
rb_define_method(cUM, "poll", UM_poll, 2);
|
|
1485
1542
|
rb_define_method(cUM, "select", UM_select, 3);
|