uringmachine 0.28.3 → 0.29.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 +10 -1
- data/TODO.md +29 -35
- data/benchmark/common.rb +6 -6
- data/benchmark/gets.rb +49 -0
- data/benchmark/{read_each.rb → output.rb} +27 -24
- data/docs/design/buffer_pool.md +35 -0
- data/docs/um_api.md +2 -0
- data/ext/um/extconf.rb +6 -5
- data/ext/um/um.c +42 -13
- data/ext/um/um.h +103 -31
- data/ext/um/um_buffer_pool.c +246 -0
- data/ext/um/um_class.c +24 -12
- data/ext/um/um_op.c +29 -13
- data/ext/um/um_ssl.c +24 -27
- data/ext/um/um_stream.c +380 -150
- data/ext/um/um_stream_class.c +119 -63
- data/ext/um/um_utils.c +6 -6
- data/grant-2025/tasks.md +12 -7
- data/lib/uringmachine/fiber_scheduler.rb +36 -10
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +60 -19
- data/test/helper.rb +2 -0
- data/test/test_fiber.rb +93 -12
- data/test/test_fiber_scheduler.rb +5 -47
- data/test/test_stream.rb +447 -125
- data/test/test_um.rb +119 -49
- metadata +5 -4
- data/ext/um/um_buffer.c +0 -49
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a6a6d78b632e43c1ac15e8745ede898e531ff9a731822fd22c5991ca0222d69
|
|
4
|
+
data.tar.gz: 0acd35ee028ea519c2788c720336dbcb1c2fa8317912459b7a81f4afbe6b3747
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed9f7684132ee1e1aae9c7783131fd927e88604444d44bc889cdf8c362b0a5dd85b3e887873c0512946af2c08dd8189e6c3bf972f19f90a7f12daee4c44c5ccd
|
|
7
|
+
data.tar.gz: f5bff069de535e906543a679266700bacbb715d0e72b14d4bf7cd042ab8d77e58d8c69feb1dbd65f16026aa4bcec20d024acffd2e2f18ac255f5b70190c25788
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
# 0.29.0 2026-03-11
|
|
2
|
+
|
|
3
|
+
- Reimplement streams based on buffer pool
|
|
4
|
+
- Add buffer pool with automatic buffer commit
|
|
5
|
+
- Add support for procs in `#await`, `#join`
|
|
6
|
+
- Accept splat fibers in `#await`
|
|
7
|
+
- Rename `#await_fibers` to `#await`
|
|
8
|
+
- Fiber scheduler: make `blocking_operation_wait` optional
|
|
9
|
+
|
|
1
10
|
# 0.28.3 2026-02-22
|
|
2
11
|
|
|
3
12
|
- Accept array in `#terminate` and `#join`
|
|
@@ -67,7 +76,7 @@
|
|
|
67
76
|
- Add `UM#metrics` for getting metrics
|
|
68
77
|
- Add `UM#pending_fibers` for detecting leaking fibers in tests
|
|
69
78
|
- More tests and benchmarks
|
|
70
|
-
- Add `UM#
|
|
79
|
+
- Add `UM#await` for awaiting fibers
|
|
71
80
|
- Add `UM.socketpair` for creating a socket pair
|
|
72
81
|
- Fix segfault caused by waiting fibers not being marked
|
|
73
82
|
- Fiber scheduler:
|
data/TODO.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
## immediate
|
|
2
2
|
|
|
3
3
|
- Add support for exception instances in `#timeout`.
|
|
4
|
-
- Add support for returning a value on timeout
|
|
4
|
+
- Add support for returning a value on timeout
|
|
5
5
|
|
|
6
6
|
Since to do this safely we need to actually raise an exception that wraps the
|
|
7
7
|
value, rescue it and return the value, we might want a separate method that
|
|
@@ -15,19 +15,22 @@
|
|
|
15
15
|
timeout(interval, timeout_error, &block)
|
|
16
16
|
rescue TimeoutValueError => e
|
|
17
17
|
raise if e != timeout_error
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
value
|
|
20
20
|
end
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
- Add tests for support for Set in `machine#
|
|
23
|
+
- Add tests for support for Set in `machine#await`
|
|
24
24
|
- Add tests for support for Set, Array in `machine#join`
|
|
25
25
|
- Add `#read_file` for reading entire file
|
|
26
26
|
- Add `#write_file` for writing entire file
|
|
27
27
|
|
|
28
28
|
- (?) Fix all futex value (Queue, Mutex) to be properly aligned
|
|
29
29
|
|
|
30
|
+
<<<<<<< HEAD
|
|
31
|
+
=======
|
|
30
32
|
## Buffer rings - automatic management
|
|
33
|
+
|
|
31
34
|
- Take the buffer_pool branch, rewrite it
|
|
32
35
|
- Allow multiple stream modes:
|
|
33
36
|
- :buffer_pool - uses buffer rings
|
|
@@ -39,13 +42,27 @@ The API will look something like:
|
|
|
39
42
|
```ruby
|
|
40
43
|
# The mode is selected automatically according to the given target
|
|
41
44
|
|
|
42
|
-
stream = UM::Stream.new(fd) # buffer_pool mode
|
|
45
|
+
stream = UM::Stream.new(machine, fd) # buffer_pool mode (read)
|
|
46
|
+
stream = UM::Stream.new(machine, fd, :recv) # buffer_pool mode (recv)
|
|
47
|
+
stream = UM::Stream.new(machine, ssl_sock) # SSLSocket mode
|
|
48
|
+
stream = UM::Stream.new(machine, conn) # IO mode
|
|
49
|
+
stream = UM::Stream.new(machine, str) # string mode
|
|
50
|
+
stream = UM::Stream.new(machine, io_buf) # IO:Buffer mode
|
|
51
|
+
```
|
|
43
52
|
|
|
44
|
-
|
|
53
|
+
This can be very useful in testing of stuff such as protocol implementations:
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
```ruby
|
|
56
|
+
stream = UM::Stream.new(machine, "GET /foo HTTP/1.1\r\nHost: bar.com\r\n")
|
|
47
57
|
```
|
|
48
58
|
|
|
59
|
+
So basically the stream is tied to a machine, and that means it can only be used
|
|
60
|
+
on the thread with which the machine is associated. It is not thread-safe. (This
|
|
61
|
+
is incidentally true also for most of the UringMachine instance methods!)
|
|
62
|
+
|
|
63
|
+
Continued discussion in docs/design/buffer_pool.md
|
|
64
|
+
|
|
65
|
+
>>>>>>> 04d9eb7 (Docs)
|
|
49
66
|
## Balancing I/O with the runqueue
|
|
50
67
|
|
|
51
68
|
- in some cases where there are many entries in the runqueue, this can
|
|
@@ -133,37 +150,14 @@ stream.read_to_eof(buf)
|
|
|
133
150
|
stream.read_to_eof(nil)
|
|
134
151
|
```
|
|
135
152
|
|
|
136
|
-
## Syntax / pattern for launching multiple operations
|
|
153
|
+
## Syntax / pattern for launching/supervising multiple operations
|
|
154
|
+
|
|
155
|
+
Select (see above):
|
|
137
156
|
|
|
138
157
|
```ruby
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
-> { ... }
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# or maybe:
|
|
146
|
-
jobs = (1..100).map { |i| -> { machine.read_file("/file_#{i}.csv") } }
|
|
147
|
-
machine.join(jobs)
|
|
148
|
-
|
|
149
|
-
# or maybe:
|
|
150
|
-
jobs = (1..100).map { |i|
|
|
151
|
-
-> {
|
|
152
|
-
pipe { machine.read_file("/file_#{i}.csv") }
|
|
153
|
-
> { csv_to_pdf(it) }
|
|
154
|
-
> { machine.write_file("/file_#{i}.pdf", it) }
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
# or otherwise
|
|
159
|
-
jobs = (1..100).map { |i|
|
|
160
|
-
-> {
|
|
161
|
-
csv = machine.read_file("/file_#{i}.csv")
|
|
162
|
-
pdf = csv_to_pdf(csv)
|
|
163
|
-
machine.write_file("/file_#{i}.pdf", pdf)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
machine.join(jobs)
|
|
158
|
+
# select
|
|
159
|
+
machine.join_select(*fibers) #=> [result, fiber]
|
|
160
|
+
machine.shift_select(*queues) #=> [result, queue]
|
|
167
161
|
```
|
|
168
162
|
|
|
169
163
|
## Other abstractions
|
data/benchmark/common.rb
CHANGED
|
@@ -200,7 +200,7 @@ class UMBenchmark
|
|
|
200
200
|
fibers = []
|
|
201
201
|
fds = []
|
|
202
202
|
do_um(machine, fibers, fds)
|
|
203
|
-
machine.
|
|
203
|
+
machine.await(fibers)
|
|
204
204
|
fds.each { machine.close(it) }
|
|
205
205
|
end
|
|
206
206
|
|
|
@@ -209,7 +209,7 @@ class UMBenchmark
|
|
|
209
209
|
fibers = []
|
|
210
210
|
fds = []
|
|
211
211
|
do_um(machine, fibers, fds)
|
|
212
|
-
machine.
|
|
212
|
+
machine.await(fibers)
|
|
213
213
|
fds.each { machine.close(it) }
|
|
214
214
|
end
|
|
215
215
|
|
|
@@ -218,7 +218,7 @@ class UMBenchmark
|
|
|
218
218
|
fibers = []
|
|
219
219
|
fds = []
|
|
220
220
|
do_um(machine, fibers, fds)
|
|
221
|
-
machine.
|
|
221
|
+
machine.await(fibers)
|
|
222
222
|
fds.each { machine.close(it) }
|
|
223
223
|
end
|
|
224
224
|
|
|
@@ -229,7 +229,7 @@ class UMBenchmark
|
|
|
229
229
|
fibers = []
|
|
230
230
|
fds = []
|
|
231
231
|
do_um_x(2, machine, fibers, fds)
|
|
232
|
-
machine.
|
|
232
|
+
machine.await(fibers)
|
|
233
233
|
fds.each { machine.close(it) }
|
|
234
234
|
end
|
|
235
235
|
end
|
|
@@ -243,7 +243,7 @@ class UMBenchmark
|
|
|
243
243
|
fibers = []
|
|
244
244
|
fds = []
|
|
245
245
|
do_um_x(4, machine, fibers, fds)
|
|
246
|
-
machine.
|
|
246
|
+
machine.await(fibers)
|
|
247
247
|
fds.each { machine.close(it) }
|
|
248
248
|
end
|
|
249
249
|
end
|
|
@@ -257,7 +257,7 @@ class UMBenchmark
|
|
|
257
257
|
fibers = []
|
|
258
258
|
fds = []
|
|
259
259
|
do_um_x(8, machine, fibers, fds)
|
|
260
|
-
machine.
|
|
260
|
+
machine.await(fibers)
|
|
261
261
|
fds.each { machine.close(it) }
|
|
262
262
|
end
|
|
263
263
|
end
|
data/benchmark/gets.rb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/inline'
|
|
4
|
+
|
|
5
|
+
gemfile do
|
|
6
|
+
source 'https://rubygems.org'
|
|
7
|
+
gem 'uringmachine', path: '..'
|
|
8
|
+
gem 'benchmark'
|
|
9
|
+
gem 'benchmark-ips'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require 'benchmark/ips'
|
|
13
|
+
require 'uringmachine'
|
|
14
|
+
|
|
15
|
+
@machine = UM.new
|
|
16
|
+
|
|
17
|
+
@io = File.open('/dev/random', 'r')
|
|
18
|
+
def io_gets
|
|
19
|
+
@io.gets
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@fd = @machine.open('/dev/random', UM::O_RDONLY)
|
|
23
|
+
@buffer = +''.encode(Encoding::US_ASCII)
|
|
24
|
+
def um_read
|
|
25
|
+
while true
|
|
26
|
+
idx = @buffer.byteindex("\n")
|
|
27
|
+
if idx
|
|
28
|
+
line = @buffer[0..(idx - 1)]
|
|
29
|
+
|
|
30
|
+
@buffer = @buffer[(idx + 1)..-1]
|
|
31
|
+
return line
|
|
32
|
+
end
|
|
33
|
+
@machine.read(@fd, @buffer, 65536, -1)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@fd_stream = @machine.open('/dev/random', UM::O_RDONLY)
|
|
38
|
+
@stream = UM::Stream.new(@machine, @fd_stream)
|
|
39
|
+
def um_stream_get_line
|
|
40
|
+
@stream.get_line(0)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Benchmark.ips do |x|
|
|
44
|
+
x.report('IO#gets') { io_gets }
|
|
45
|
+
x.report('UM#read+buf') { um_read }
|
|
46
|
+
x.report('UM::Stream') { um_stream_get_line }
|
|
47
|
+
|
|
48
|
+
x.compare!(order: :baseline)
|
|
49
|
+
end
|
|
@@ -13,42 +13,31 @@ require 'uringmachine'
|
|
|
13
13
|
|
|
14
14
|
@machine = UM.new
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
port = 10000 + rand(30000)
|
|
18
|
-
server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
19
|
-
@machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
20
|
-
@machine.bind(server_fd, '127.0.0.1', port)
|
|
21
|
-
@machine.listen(server_fd, UM::SOMAXCONN)
|
|
22
|
-
|
|
23
|
-
client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
24
|
-
@machine.connect(client_conn_fd, '127.0.0.1', port)
|
|
25
|
-
|
|
26
|
-
server_conn_fd = @machine.accept(server_fd)
|
|
27
|
-
|
|
28
|
-
@machine.close(server_fd)
|
|
29
|
-
[client_conn_fd, server_conn_fd]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
@client_fd, @server_fd = make_socket_pair.()
|
|
16
|
+
@client_fd, @server_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
|
|
33
17
|
|
|
34
18
|
@read_buf = +''
|
|
35
19
|
@read_fiber = @machine.spin do
|
|
36
20
|
while true
|
|
37
21
|
@machine.read(@client_fd, @read_buf, 65536, 0)
|
|
38
22
|
end
|
|
23
|
+
rescue Exception => e
|
|
24
|
+
p e
|
|
25
|
+
p e.backtrace.join
|
|
26
|
+
exit!
|
|
39
27
|
end
|
|
40
28
|
|
|
41
29
|
STR_COUNT = ARGV[0]&.to_i || 3
|
|
42
30
|
STR_SIZE = ARGV[1]&.to_i || 100
|
|
43
31
|
|
|
44
32
|
@parts = ['*' * STR_SIZE] * STR_COUNT
|
|
33
|
+
p @parts
|
|
45
34
|
|
|
46
|
-
@server_io = IO.new(@server_fd)
|
|
47
|
-
@server_io.sync = true
|
|
48
|
-
def io_write
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
35
|
+
# @server_io = IO.new(@server_fd)
|
|
36
|
+
# @server_io.sync = true
|
|
37
|
+
# def io_write
|
|
38
|
+
# @server_io.write(*@parts)
|
|
39
|
+
# 10.times { @machine.snooze }
|
|
40
|
+
# end
|
|
52
41
|
|
|
53
42
|
def um_write
|
|
54
43
|
str = @parts.join
|
|
@@ -61,6 +50,17 @@ def um_write
|
|
|
61
50
|
end
|
|
62
51
|
end
|
|
63
52
|
|
|
53
|
+
# def um_writev
|
|
54
|
+
# str = @parts.join
|
|
55
|
+
# len = str.bytesize
|
|
56
|
+
|
|
57
|
+
# while len > 0
|
|
58
|
+
# ret = @machine.write(@server_fd, str, len)
|
|
59
|
+
# len -= ret
|
|
60
|
+
# str = str[ret..-1] if len > 0
|
|
61
|
+
# end
|
|
62
|
+
# end
|
|
63
|
+
|
|
64
64
|
def um_send
|
|
65
65
|
str = @parts.join
|
|
66
66
|
@machine.send(@server_fd, str, str.bytesize, UM::MSG_WAITALL)
|
|
@@ -73,8 +73,11 @@ end
|
|
|
73
73
|
|
|
74
74
|
p(STR_COUNT:, STR_SIZE:)
|
|
75
75
|
|
|
76
|
+
# 10.times { io_write }
|
|
77
|
+
# @machine.sleep(0.5)
|
|
78
|
+
|
|
76
79
|
Benchmark.ips do |x|
|
|
77
|
-
x.report('IO#write') { io_write }
|
|
80
|
+
# x.report('IO#write') { io_write }
|
|
78
81
|
x.report('UM#write') { um_write }
|
|
79
82
|
x.report('UM#send') { um_send }
|
|
80
83
|
x.report('UM#send_bundle') { um_send_bundle }
|
data/docs/design/buffer_pool.md
CHANGED
|
@@ -20,9 +20,44 @@ buffers, to using managed buffers from the buffer pool.
|
|
|
20
20
|
|
|
21
21
|
## Design
|
|
22
22
|
|
|
23
|
+
### Goals
|
|
24
|
+
|
|
25
|
+
- We want an abstract design that will work with both multishot read/recv and
|
|
26
|
+
automatic buffer management, *and* other readable sources, such as an SSL
|
|
27
|
+
socket, an `IO` instance, or even a string or a `IO::Buffer`.
|
|
28
|
+
|
|
29
|
+
### The Stream Data Model
|
|
30
|
+
|
|
31
|
+
- A stream is a series of `segments` (containing a `ptr` and `len`), held in a
|
|
32
|
+
linked list. A segment also has a `type` and a potential backing store
|
|
33
|
+
reference. For example, a segment with a `SEGMENT_STRING` type points to a
|
|
34
|
+
`String` object. A segment with a `SEGMENT_BUFFER_POOL` has a reference to a
|
|
35
|
+
managed buffer from the buffer pool with the given id.
|
|
36
|
+
- The buffer pool is linked list of buffer descriptors, each referencing a piece
|
|
37
|
+
of memory of a certain length, along with some metadata.
|
|
38
|
+
- Buffer descriptors are allocated in batches as needed and added to the buffer
|
|
39
|
+
pool.
|
|
40
|
+
- A buffer group describes a buffer ring. It has a certain size, which limits
|
|
41
|
+
the number of buffers that can be added to the buffer ring.
|
|
42
|
+
- When a buffer group is used for a multishot read/recv, buffers will be
|
|
43
|
+
automatically checked out from the buffer pool and added to the buffer ring
|
|
44
|
+
and commited to the kernel. The buffer group allocates and tracks a buffer id
|
|
45
|
+
for each buffer descriptor.
|
|
46
|
+
|
|
47
|
+
### How buffer groups are checked out
|
|
48
|
+
|
|
49
|
+
- We need a freelist of buffer groups
|
|
50
|
+
|
|
51
|
+
- Once a segment is completely consumed, some cleanup code might be needed (for
|
|
52
|
+
example to decrement a ref_count on a managed buffer, or unlocking a string
|
|
53
|
+
backing store.)
|
|
54
|
+
|
|
55
|
+
|
|
23
56
|
### The API
|
|
24
57
|
|
|
25
58
|
- The buffer pool is created and managed automatically. No API is involved.
|
|
59
|
+
-
|
|
60
|
+
-
|
|
26
61
|
|
|
27
62
|
- To use the buffer pool, two dedicated APIs are added:
|
|
28
63
|
|
data/docs/um_api.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- `accept_into_queue(sockfd, queue)` - accepts incoming connections to the given
|
|
23
23
|
server socket in an infinite loop, pushing each one to the given queue.
|
|
24
24
|
- `accept(sockfd)` - accepts an incoming connection, returning its fd.
|
|
25
|
+
- `await(*fibers)` - awaits fiber termination
|
|
25
26
|
- `bind(sockfd, host, port)` - binds the given socket to the given address.
|
|
26
27
|
- `close_async(fd)` - closes the given fd asynchronously, i.e. <w>ithout waiting
|
|
27
28
|
for the operation to complete.
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
- `connect(sockfd, host, port)` - connects the given socket to the given
|
|
30
31
|
address.
|
|
31
32
|
- `getsockopt(sockfd, level, opt)` - returns a socket option value.
|
|
33
|
+
- `join(*fibers)` - waits for fibers to terminate and returns return values.
|
|
32
34
|
- `listen(sockfd)` - starts listening on the given socket.
|
|
33
35
|
- `metrics` - returns metrics for the machine.
|
|
34
36
|
- `open(pathname, flags)` - opens the given path and returns an fd.
|
data/ext/um/extconf.rb
CHANGED
|
@@ -30,9 +30,7 @@ def get_config
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
config = get_config
|
|
33
|
-
puts "Building UringMachine (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
|
|
34
|
-
|
|
35
|
-
# require_relative 'zlib_conf'
|
|
33
|
+
STDERR.puts "Building UringMachine (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
|
|
36
34
|
|
|
37
35
|
liburing_path = File.expand_path('../../vendor/liburing', __dir__)
|
|
38
36
|
FileUtils.cd liburing_path do
|
|
@@ -76,8 +74,11 @@ $defs << '-DHAVE_IO_URING_PREP_BIND' if config[:prep_bind]
|
|
|
76
74
|
$defs << '-DHAVE_IO_URING_PREP_LISTEN' if config[:prep_listen]
|
|
77
75
|
$defs << '-DHAVE_IO_URING_SEND_VECTORIZED' if config[:send_vectoized]
|
|
78
76
|
|
|
79
|
-
$CFLAGS << ' -
|
|
77
|
+
$CFLAGS << ' -Werror -Wall -Wextra'
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
if ENV['SANITIZE']
|
|
80
|
+
$CFLAGS << ' -fsanitize=undefined,address -lasan'
|
|
81
|
+
$LDFLAGS << ' -fsanitize=undefined,address -lasan'
|
|
82
|
+
end
|
|
82
83
|
|
|
83
84
|
create_makefile 'um_ext'
|
data/ext/um/um.c
CHANGED
|
@@ -38,6 +38,8 @@ void um_setup(VALUE self, struct um *machine, uint size, uint sqpoll_timeout_mse
|
|
|
38
38
|
if (ret) rb_syserr_fail(-ret, strerror(-ret));
|
|
39
39
|
machine->ring_initialized = 1;
|
|
40
40
|
|
|
41
|
+
bp_setup(machine);
|
|
42
|
+
|
|
41
43
|
if (machine->sidecar_mode) um_sidecar_setup(machine);
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -56,7 +58,8 @@ inline void um_teardown(struct um *machine) {
|
|
|
56
58
|
io_uring_queue_exit(&machine->ring);
|
|
57
59
|
machine->ring_initialized = 0;
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
bp_discard_buffer_freelist(machine);
|
|
62
|
+
bp_teardown(machine);
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
inline struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op) {
|
|
@@ -981,7 +984,7 @@ struct send_recv_fd_ctx {
|
|
|
981
984
|
struct msghdr msgh;
|
|
982
985
|
char iobuf[1];
|
|
983
986
|
struct iovec iov;
|
|
984
|
-
union { // Ancillary data buffer, wrapped in a union for alignment
|
|
987
|
+
union { // Ancillary data buffer, wrapped in a union for alignment
|
|
985
988
|
char buf[CMSG_SPACE(sizeof(int))];
|
|
986
989
|
struct cmsghdr align;
|
|
987
990
|
} u;
|
|
@@ -1296,6 +1299,13 @@ VALUE accept_each_start(VALUE arg) {
|
|
|
1296
1299
|
struct um_op_result *result = &ctx->op->result;
|
|
1297
1300
|
while (result) {
|
|
1298
1301
|
if (unlikely(result->res < 0)) {
|
|
1302
|
+
if (result->res == -EINVAL) {
|
|
1303
|
+
// An EINVAL indicates the socket was shutdown, so we just break of of
|
|
1304
|
+
// the loop. We also need to prevent raising error in
|
|
1305
|
+
// um_verify_op_completion.
|
|
1306
|
+
result->res = 0;
|
|
1307
|
+
break;
|
|
1308
|
+
}
|
|
1299
1309
|
rb_syserr_fail(-result->res, strerror(-result->res));
|
|
1300
1310
|
}
|
|
1301
1311
|
rb_yield(INT2NUM(result->res));
|
|
@@ -1326,6 +1336,13 @@ VALUE accept_into_queue_start(VALUE arg) {
|
|
|
1326
1336
|
struct um_op_result *result = &ctx->op->result;
|
|
1327
1337
|
while (result) {
|
|
1328
1338
|
if (unlikely(result->res < 0)) {
|
|
1339
|
+
if (result->res == -EINVAL) {
|
|
1340
|
+
// An EINVAL indicates the socket was shutdown, so we just break of of
|
|
1341
|
+
// the loop. We also need to prevent raising error in
|
|
1342
|
+
// um_verify_op_completion.
|
|
1343
|
+
result->res = 0;
|
|
1344
|
+
break;
|
|
1345
|
+
}
|
|
1329
1346
|
rb_syserr_fail(-result->res, strerror(-result->res));
|
|
1330
1347
|
}
|
|
1331
1348
|
um_queue_push(ctx->machine, ctx->queue, INT2NUM(result->res));
|
|
@@ -1533,26 +1550,38 @@ extern VALUE SYM_ops_free;
|
|
|
1533
1550
|
extern VALUE SYM_ops_transient;
|
|
1534
1551
|
extern VALUE SYM_time_total_cpu;
|
|
1535
1552
|
extern VALUE SYM_time_total_wait;
|
|
1553
|
+
extern VALUE SYM_buffer_groups;
|
|
1554
|
+
extern VALUE SYM_buffers_allocated;
|
|
1555
|
+
extern VALUE SYM_buffers_free;
|
|
1556
|
+
extern VALUE SYM_segments_free;
|
|
1557
|
+
extern VALUE SYM_buffer_space_allocated;
|
|
1558
|
+
extern VALUE SYM_buffer_space_commited;
|
|
1536
1559
|
|
|
1537
1560
|
VALUE um_metrics(struct um *machine, struct um_metrics *metrics) {
|
|
1538
1561
|
VALUE hash = rb_hash_new();
|
|
1539
1562
|
|
|
1540
|
-
rb_hash_aset(hash, SYM_size,
|
|
1563
|
+
rb_hash_aset(hash, SYM_size, UINT2NUM(machine->size));
|
|
1564
|
+
|
|
1565
|
+
rb_hash_aset(hash, SYM_total_ops, ULONG2NUM(metrics->total_ops));
|
|
1566
|
+
rb_hash_aset(hash, SYM_total_switches, ULONG2NUM(metrics->total_switches));
|
|
1567
|
+
rb_hash_aset(hash, SYM_total_waits, ULONG2NUM(metrics->total_waits));
|
|
1541
1568
|
|
|
1542
|
-
rb_hash_aset(hash,
|
|
1543
|
-
rb_hash_aset(hash,
|
|
1544
|
-
rb_hash_aset(hash,
|
|
1569
|
+
rb_hash_aset(hash, SYM_ops_pending, UINT2NUM(metrics->ops_pending));
|
|
1570
|
+
rb_hash_aset(hash, SYM_ops_unsubmitted, UINT2NUM(metrics->ops_unsubmitted));
|
|
1571
|
+
rb_hash_aset(hash, SYM_ops_runqueue, UINT2NUM(metrics->ops_runqueue));
|
|
1572
|
+
rb_hash_aset(hash, SYM_ops_free, UINT2NUM(metrics->ops_free));
|
|
1573
|
+
rb_hash_aset(hash, SYM_ops_transient, UINT2NUM(metrics->ops_transient));
|
|
1545
1574
|
|
|
1546
|
-
rb_hash_aset(hash,
|
|
1547
|
-
rb_hash_aset(hash,
|
|
1548
|
-
rb_hash_aset(hash,
|
|
1549
|
-
rb_hash_aset(hash,
|
|
1550
|
-
rb_hash_aset(hash,
|
|
1575
|
+
rb_hash_aset(hash, SYM_buffers_allocated, UINT2NUM(metrics->buffers_allocated));
|
|
1576
|
+
rb_hash_aset(hash, SYM_buffers_free, UINT2NUM(metrics->buffers_free));
|
|
1577
|
+
rb_hash_aset(hash, SYM_segments_free, UINT2NUM(metrics->segments_free));
|
|
1578
|
+
rb_hash_aset(hash, SYM_buffer_space_allocated, ULONG2NUM(metrics->buffer_space_allocated));
|
|
1579
|
+
rb_hash_aset(hash, SYM_buffer_space_commited, ULONG2NUM(metrics->buffer_space_commited));
|
|
1551
1580
|
|
|
1552
1581
|
if (machine->profile_mode) {
|
|
1553
1582
|
double total_cpu = um_get_time_cpu() - metrics->time_first_cpu;
|
|
1554
|
-
rb_hash_aset(hash, SYM_time_total_cpu,
|
|
1555
|
-
rb_hash_aset(hash, SYM_time_total_wait,
|
|
1583
|
+
rb_hash_aset(hash, SYM_time_total_cpu, DBL2NUM(total_cpu));
|
|
1584
|
+
rb_hash_aset(hash, SYM_time_total_wait, DBL2NUM(metrics->time_total_wait));
|
|
1556
1585
|
}
|
|
1557
1586
|
|
|
1558
1587
|
return hash;
|