uringmachine 0.29.2 → 0.31.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 +18 -4
- data/README.md +46 -38
- data/TODO.md +68 -75
- data/benchmark/bm_io_ssl.rb +128 -0
- data/benchmark/bm_redis_client.rb +76 -0
- data/benchmark/common.rb +1 -0
- data/benchmark/gets.rb +7 -7
- data/benchmark/gets_concurrent.rb +10 -10
- data/benchmark/http_parse.rb +15 -15
- data/benchmark/http_server_accept_queue.rb +11 -7
- data/benchmark/http_server_multi_accept.rb +7 -7
- data/benchmark/http_server_multi_ractor.rb +7 -7
- data/benchmark/http_server_single_thread.rb +7 -7
- data/benchmark/openssl.rb +50 -22
- data/docs/design/buffer_pool.md +1 -1
- data/examples/fiber_concurrency_io.rb +52 -0
- data/examples/fiber_concurrency_naive.rb +26 -0
- data/examples/fiber_concurrency_runqueue.rb +33 -0
- data/examples/io_uring_simple.c +24 -0
- data/examples/pg.rb +2 -2
- data/examples/stream.rb +2 -2
- data/ext/um/um.c +71 -3
- data/ext/um/um.h +34 -22
- data/ext/um/um_buffer_pool.c +11 -11
- data/ext/um/um_class.c +57 -0
- data/ext/um/um_connection.c +775 -0
- data/ext/um/um_connection_class.c +394 -0
- data/ext/um/um_ssl.c +43 -7
- data/ext/um/um_utils.c +1 -1
- data/grant-2025/final-report.md +269 -0
- data/grant-2025/journal.md +1 -1
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +47 -6
- data/test/{test_stream.rb → test_connection.rb} +321 -151
- data/test/test_ssl.rb +27 -0
- data/test/test_um.rb +174 -17
- metadata +13 -6
- data/ext/um/um_stream.c +0 -674
- data/ext/um/um_stream_class.c +0 -303
data/benchmark/http_parse.rb
CHANGED
|
@@ -62,10 +62,10 @@ 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
|
-
def
|
|
68
|
+
def read_line(fd, sio, buffer)
|
|
69
69
|
while true
|
|
70
70
|
line = sio.gets(chomp: true)
|
|
71
71
|
return line if line
|
|
@@ -76,7 +76,7 @@ def get_line(fd, sio, buffer)
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def get_request_line(fd, sio, buffer)
|
|
79
|
-
line =
|
|
79
|
+
line = read_line(fd, sio, buffer)
|
|
80
80
|
|
|
81
81
|
m = line.match(RE_REQUEST_LINE)
|
|
82
82
|
return nil if !m
|
|
@@ -96,7 +96,7 @@ def parse_headers(fd)
|
|
|
96
96
|
return nil if !headers
|
|
97
97
|
|
|
98
98
|
while true
|
|
99
|
-
line =
|
|
99
|
+
line = read_line(fd, sio, buffer)
|
|
100
100
|
break if line.empty?
|
|
101
101
|
|
|
102
102
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -129,15 +129,15 @@ ensure
|
|
|
129
129
|
($machine.close(wfd) rescue nil) if wfd
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
def
|
|
133
|
-
|
|
132
|
+
def connection_parse_headers(fd)
|
|
133
|
+
conn = UM::Connection.new($machine, fd)
|
|
134
134
|
|
|
135
135
|
buf = String.new(capacity: 65536)
|
|
136
|
-
headers =
|
|
136
|
+
headers = connection_get_request_line(conn, buf)
|
|
137
137
|
return nil if !headers
|
|
138
138
|
|
|
139
139
|
while true
|
|
140
|
-
line =
|
|
140
|
+
line = conn.read_line(0)
|
|
141
141
|
break if line.empty?
|
|
142
142
|
|
|
143
143
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -149,8 +149,8 @@ def stream_parse_headers(fd)
|
|
|
149
149
|
headers
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
def
|
|
153
|
-
line =
|
|
152
|
+
def connection_get_request_line(conn, buf)
|
|
153
|
+
line = conn.read_line(0)
|
|
154
154
|
|
|
155
155
|
m = line.match(RE_REQUEST_LINE)
|
|
156
156
|
return nil if !m
|
|
@@ -162,12 +162,12 @@ def stream_get_request_line(stream, buf)
|
|
|
162
162
|
}
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
-
def
|
|
165
|
+
def parse_http_connection
|
|
166
166
|
rfd, wfd = UM.pipe
|
|
167
167
|
queue = UM::Queue.new
|
|
168
168
|
|
|
169
169
|
$machine.spin do
|
|
170
|
-
headers =
|
|
170
|
+
headers = connection_parse_headers(rfd)
|
|
171
171
|
$machine.push(queue, headers)
|
|
172
172
|
rescue Exception => e
|
|
173
173
|
p e
|
|
@@ -188,7 +188,7 @@ def compare_allocs
|
|
|
188
188
|
p(
|
|
189
189
|
alloc_http_parser: alloc_count { x.times { parse_http_parser } },
|
|
190
190
|
alloc_stringio: alloc_count { x.times { parse_http_stringio } },
|
|
191
|
-
|
|
191
|
+
alloc_connection: alloc_count { x.times { parse_http_connection } }
|
|
192
192
|
)
|
|
193
193
|
ensure
|
|
194
194
|
GC.enable
|
|
@@ -213,8 +213,8 @@ def benchmark
|
|
|
213
213
|
x.config(:time => 5, :warmup => 3)
|
|
214
214
|
|
|
215
215
|
x.report("http_parser") { parse_http_parser }
|
|
216
|
-
x.report("stringio")
|
|
217
|
-
x.report("
|
|
216
|
+
x.report("stringio") { parse_http_stringio }
|
|
217
|
+
x.report("connection") { parse_http_connection }
|
|
218
218
|
|
|
219
219
|
x.compare!
|
|
220
220
|
end
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -51,17 +51,21 @@ def send_response(machine, fd)
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def handle_connection(machine, fd)
|
|
54
|
-
|
|
54
|
+
conn = UM::Connection.new(machine, fd)
|
|
55
55
|
buf = String.new(capacity: 65536)
|
|
56
56
|
|
|
57
57
|
while true
|
|
58
|
-
headers = get_headers(
|
|
58
|
+
headers = get_headers(conn, buf)
|
|
59
59
|
break if !headers
|
|
60
60
|
|
|
61
61
|
send_response(machine, fd)
|
|
62
62
|
end
|
|
63
63
|
rescue InvalidHeadersError, SystemCallError => e
|
|
64
64
|
# ignore
|
|
65
|
+
rescue => e
|
|
66
|
+
p e
|
|
67
|
+
p e.backtrace
|
|
68
|
+
exit!
|
|
65
69
|
ensure
|
|
66
70
|
machine.close_async(fd)
|
|
67
71
|
end
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -51,11 +51,11 @@ def send_response(machine, fd)
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def handle_connection(machine, fd)
|
|
54
|
-
|
|
54
|
+
conn = UM::Connection.new(machine, fd)
|
|
55
55
|
buf = String.new(capacity: 65536)
|
|
56
56
|
|
|
57
57
|
while true
|
|
58
|
-
headers = get_headers(
|
|
58
|
+
headers = get_headers(conn, buf)
|
|
59
59
|
break if !headers
|
|
60
60
|
|
|
61
61
|
send_response(machine, fd)
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -53,11 +53,11 @@ end
|
|
|
53
53
|
|
|
54
54
|
def handle_connection(machine, fd)
|
|
55
55
|
machine.setsockopt(fd, UM::IPPROTO_TCP, UM::TCP_NODELAY, true)
|
|
56
|
-
|
|
56
|
+
conn = UM::Connection.new(machine, fd)
|
|
57
57
|
buf = String.new(capacity: 65536)
|
|
58
58
|
|
|
59
59
|
while true
|
|
60
|
-
headers = get_headers(
|
|
60
|
+
headers = get_headers(conn, buf)
|
|
61
61
|
break if !headers
|
|
62
62
|
|
|
63
63
|
send_response(machine, fd)
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -52,11 +52,11 @@ end
|
|
|
52
52
|
|
|
53
53
|
def handle_connection(machine, fd)
|
|
54
54
|
machine.setsockopt(fd, UM::IPPROTO_TCP, UM::TCP_NODELAY, true)
|
|
55
|
-
|
|
55
|
+
conn = UM::Connection.new(machine, fd)
|
|
56
56
|
buf = String.new(capacity: 65536)
|
|
57
57
|
|
|
58
58
|
while true
|
|
59
|
-
headers = get_headers(
|
|
59
|
+
headers = get_headers(conn, buf)
|
|
60
60
|
break if !headers
|
|
61
61
|
|
|
62
62
|
send_response(machine, fd)
|
data/benchmark/openssl.rb
CHANGED
|
@@ -24,54 +24,82 @@ Socket.do_not_reverse_lookup = true
|
|
|
24
24
|
tcps = TCPServer.new("127.0.0.1", 0)
|
|
25
25
|
port = tcps.connect_address.ip_port
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Thread.new do
|
|
30
|
-
Thread.current.report_on_exception = false
|
|
31
|
-
loop do
|
|
32
|
-
begin
|
|
33
|
-
ssl = ssls.accept
|
|
34
|
-
rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
|
|
35
|
-
Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
|
|
36
|
-
retry
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
Thread.new do
|
|
40
|
-
Thread.current.report_on_exception = false
|
|
27
|
+
pid = fork do
|
|
28
|
+
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
|
|
41
29
|
|
|
30
|
+
Thread.new do
|
|
31
|
+
Thread.current.report_on_exception = false
|
|
32
|
+
loop do
|
|
42
33
|
begin
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
ssl = ssls.accept
|
|
35
|
+
rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
|
|
36
|
+
Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
|
|
37
|
+
retry
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Thread.new do
|
|
41
|
+
Thread.current.report_on_exception = false
|
|
42
|
+
|
|
43
|
+
begin
|
|
44
|
+
while line = ssl.gets
|
|
45
|
+
ssl.write(line)
|
|
46
|
+
end
|
|
47
|
+
ensure
|
|
48
|
+
ssl.close
|
|
45
49
|
end
|
|
46
|
-
|
|
47
|
-
ssl.close
|
|
50
|
+
true
|
|
48
51
|
end
|
|
49
|
-
true
|
|
50
52
|
end
|
|
51
53
|
end
|
|
54
|
+
sleep
|
|
55
|
+
rescue Interrupt
|
|
52
56
|
end
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
at_exit {
|
|
59
|
+
Process.kill('SIGINT', pid)
|
|
60
|
+
Process.wait(pid)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
@ssl_stock = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port)
|
|
65
|
+
rescue => e
|
|
66
|
+
p e
|
|
67
|
+
exit!
|
|
68
|
+
end
|
|
55
69
|
@ssl_stock.sync_close = true
|
|
56
70
|
@ssl_stock.connect
|
|
57
71
|
|
|
58
|
-
um = UM.new
|
|
72
|
+
@um = UM.new
|
|
59
73
|
|
|
60
74
|
@ssl_um = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port)
|
|
61
75
|
@ssl_um.sync_close = true
|
|
62
|
-
um.ssl_set_bio(@ssl_um)
|
|
76
|
+
@um.ssl_set_bio(@ssl_um)
|
|
63
77
|
@ssl_um.connect
|
|
64
78
|
|
|
79
|
+
@ssl_conn = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port)
|
|
80
|
+
@ssl_conn.sync_close = true
|
|
81
|
+
@um.ssl_set_bio(@ssl_conn)
|
|
82
|
+
@ssl_conn.connect
|
|
83
|
+
|
|
84
|
+
@conn = @um.connection(@ssl_conn, :ssl)
|
|
85
|
+
|
|
65
86
|
@msg = 'abc' * 1000
|
|
87
|
+
@msg_newline = @msg + "\n"
|
|
66
88
|
|
|
67
89
|
def do_io(ssl)
|
|
68
90
|
ssl.puts @msg
|
|
69
91
|
ssl.gets
|
|
70
92
|
end
|
|
71
93
|
|
|
94
|
+
def do_io_connection(ssl, um, conn)
|
|
95
|
+
um.ssl_write(ssl, @msg_newline, 0)
|
|
96
|
+
conn.read_line(0)
|
|
97
|
+
end
|
|
98
|
+
|
|
72
99
|
Benchmark.ips do |x|
|
|
73
100
|
x.report('stock') { do_io(@ssl_stock) }
|
|
74
101
|
x.report('UM BIO') { do_io(@ssl_um) }
|
|
102
|
+
x.report('UM Stream') { do_io_connection(@ssl_conn, @um, @conn) }
|
|
75
103
|
|
|
76
104
|
x.compare!(order: :baseline)
|
|
77
105
|
end
|
data/docs/design/buffer_pool.md
CHANGED
|
@@ -78,7 +78,7 @@ buffers, to using managed buffers from the buffer pool.
|
|
|
78
78
|
```ruby
|
|
79
79
|
machine.stream_recv(fd) do |stream|
|
|
80
80
|
loop do
|
|
81
|
-
line = stream.
|
|
81
|
+
line = stream.read_line(max: 60)
|
|
82
82
|
if (size = parse_size(line))
|
|
83
83
|
data = stream.read(size)
|
|
84
84
|
process_data(data)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@runqueue = []
|
|
2
|
+
@active_reads = {}; @active_timers = {}
|
|
3
|
+
|
|
4
|
+
def fiber_switch
|
|
5
|
+
while true
|
|
6
|
+
next_fiber, value = @runqueue.shift
|
|
7
|
+
return next_fiber.transfer value if next_fiber
|
|
8
|
+
|
|
9
|
+
process_events
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def process_events
|
|
14
|
+
shortest_timeout = @active_timers.values.min - Time.now
|
|
15
|
+
process_reads(shortest_timeout)
|
|
16
|
+
now = Time.now
|
|
17
|
+
@active_timers.select { |_, t| t <= now }.keys.each {
|
|
18
|
+
@runqueue << it
|
|
19
|
+
@active_timers.delete(it)
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def process_reads(timeout)
|
|
24
|
+
r, _ = IO.select(@active_reads.keys, [], [], timeout)
|
|
25
|
+
r&.each { |io|
|
|
26
|
+
@runqueue << [@active_reads[io], io.readpartial(256)]
|
|
27
|
+
@active_reads.delete(io)
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def do_read(io)
|
|
32
|
+
@active_reads[io] = Fiber.current
|
|
33
|
+
fiber_switch
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def do_sleep(time)
|
|
37
|
+
fiber = Fiber.current
|
|
38
|
+
@active_timers[fiber] = Time.now + time
|
|
39
|
+
fiber_switch
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@runqueue << Fiber.new {
|
|
43
|
+
input = do_read(STDIN)
|
|
44
|
+
puts "Got: #{input}"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@runqueue << Fiber.new {
|
|
48
|
+
do_sleep(5)
|
|
49
|
+
puts "5 seconds have elapsed!"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fiber_switch
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@fiber1 = Fiber.new {
|
|
2
|
+
count = 0
|
|
3
|
+
# read from STDIN
|
|
4
|
+
while true
|
|
5
|
+
count += 1
|
|
6
|
+
input = STDIN.read_nonblock(256, exception: false)
|
|
7
|
+
if input == :wait_readable
|
|
8
|
+
@fiber2.transfer
|
|
9
|
+
else
|
|
10
|
+
break
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
puts "Got: #{input} after #{count} tries"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@fiber2 = Fiber.new {
|
|
17
|
+
last = Time.now
|
|
18
|
+
|
|
19
|
+
# sleep
|
|
20
|
+
while Time.now < last + 5
|
|
21
|
+
@fiber1.transfer
|
|
22
|
+
end
|
|
23
|
+
STDOUT << "5 seconds have elapsed!\n"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@fiber1.transfer
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@runqueue = []
|
|
2
|
+
def fiber_switch
|
|
3
|
+
next_fiber = @runqueue.shift
|
|
4
|
+
@runqueue << Fiber.current
|
|
5
|
+
next_fiber.transfer
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
@runqueue << Fiber.new {
|
|
9
|
+
count = 0
|
|
10
|
+
# read from STDIN
|
|
11
|
+
while true
|
|
12
|
+
count += 1
|
|
13
|
+
input = STDIN.read_nonblock(256, exception: false)
|
|
14
|
+
if input == :wait_readable
|
|
15
|
+
fiber_switch
|
|
16
|
+
else
|
|
17
|
+
break
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
puts "Got: #{input} after #{count} tries"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@runqueue << Fiber.new {
|
|
24
|
+
last = Time.now
|
|
25
|
+
|
|
26
|
+
# sleep
|
|
27
|
+
while Time.now < last + 10
|
|
28
|
+
fiber_switch
|
|
29
|
+
end
|
|
30
|
+
STDOUT << "10 seconds have elapsed!\n"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@runqueue.shift.transfer
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#include <liburing.h>
|
|
2
|
+
|
|
3
|
+
struct io_uring ring;
|
|
4
|
+
struct io_uring_sqe *sqe;
|
|
5
|
+
struct io_uring_cqe *cqe;
|
|
6
|
+
int ret;
|
|
7
|
+
char buf[100];
|
|
8
|
+
|
|
9
|
+
ret = io_uring_queue_init(8, &ring, 0);
|
|
10
|
+
sqe = io_uring_get_sqe(&ring);
|
|
11
|
+
io_uring_prep_read(sqe, STDIN_FILENO, buf, 100, 0);
|
|
12
|
+
sqe->user_data = 42;
|
|
13
|
+
io_uring_submit(&ring);
|
|
14
|
+
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
ret = io_uring_wait_cqe(&ring, &cqe);
|
|
18
|
+
if (!ret) {
|
|
19
|
+
if (cqe->user_data == 42) {
|
|
20
|
+
int len = cqe->res;
|
|
21
|
+
printf("Got: %d\n", len);
|
|
22
|
+
}
|
|
23
|
+
io_uring_cqe_seen(&ring, cqe);
|
|
24
|
+
}
|
data/examples/pg.rb
CHANGED
|
@@ -66,9 +66,9 @@ $machine.listen(server_fd, UM::SOMAXCONN)
|
|
|
66
66
|
puts 'Listening on port 1234'
|
|
67
67
|
|
|
68
68
|
def handle_connection(fd)
|
|
69
|
-
|
|
69
|
+
conn = UM::Connection.new($machine, fd)
|
|
70
70
|
|
|
71
|
-
while (l =
|
|
71
|
+
while (l = conn.gets)
|
|
72
72
|
$machine.write(fd, "You said: #{l}")
|
|
73
73
|
end
|
|
74
74
|
rescue Exception => e
|
data/examples/stream.rb
CHANGED
|
@@ -66,9 +66,9 @@ $machine.listen(server_fd, UM::SOMAXCONN)
|
|
|
66
66
|
puts 'Listening on port 1234'
|
|
67
67
|
|
|
68
68
|
def handle_connection(fd)
|
|
69
|
-
|
|
69
|
+
conn = UM::Stream.new($machine, fd)
|
|
70
70
|
|
|
71
|
-
while (l =
|
|
71
|
+
while (l = conn.gets)
|
|
72
72
|
$machine.write(fd, "You said: #{l}")
|
|
73
73
|
end
|
|
74
74
|
rescue Exception => e
|
data/ext/um/um.c
CHANGED
|
@@ -652,11 +652,11 @@ VALUE um_write(struct um *machine, int fd, VALUE buffer, size_t len, __u64 file_
|
|
|
652
652
|
return ret;
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
-
size_t um_write_raw(struct um *machine, int fd, const char *buffer, size_t
|
|
655
|
+
size_t um_write_raw(struct um *machine, int fd, const char *buffer, size_t len) {
|
|
656
656
|
struct um_op *op = um_op_acquire(machine);
|
|
657
657
|
um_prep_op(machine, op, OP_WRITE, 2, 0);
|
|
658
658
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
659
|
-
io_uring_prep_write(sqe, fd, buffer,
|
|
659
|
+
io_uring_prep_write(sqe, fd, buffer, len, 0);
|
|
660
660
|
|
|
661
661
|
int res = 0;
|
|
662
662
|
VALUE ret = um_yield(machine);
|
|
@@ -831,6 +831,23 @@ VALUE um_send(struct um *machine, int fd, VALUE buffer, size_t len, int flags) {
|
|
|
831
831
|
return ret;
|
|
832
832
|
}
|
|
833
833
|
|
|
834
|
+
size_t um_send_raw(struct um *machine, int fd, const char *buffer, size_t len, int flags) {
|
|
835
|
+
struct um_op *op = um_op_acquire(machine);
|
|
836
|
+
um_prep_op(machine, op, OP_SEND, 2, 0);
|
|
837
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
838
|
+
io_uring_prep_send(sqe, fd, buffer, len, flags);
|
|
839
|
+
|
|
840
|
+
int res = 0;
|
|
841
|
+
VALUE ret = um_yield(machine);
|
|
842
|
+
|
|
843
|
+
if (likely(um_verify_op_completion(machine, op, true))) res = op->result.res;
|
|
844
|
+
um_op_release(machine, op);
|
|
845
|
+
|
|
846
|
+
RAISE_IF_EXCEPTION(ret);
|
|
847
|
+
RB_GC_GUARD(ret);
|
|
848
|
+
return res;
|
|
849
|
+
}
|
|
850
|
+
|
|
834
851
|
// for some reason we don't get this define from liburing/io_uring.h
|
|
835
852
|
#define IORING_SEND_VECTORIZED (1U << 5)
|
|
836
853
|
|
|
@@ -1285,6 +1302,57 @@ VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned in
|
|
|
1285
1302
|
return statx_to_hash(&stat);
|
|
1286
1303
|
}
|
|
1287
1304
|
|
|
1305
|
+
VALUE um_splice(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_SPLICE, 2, 0);
|
|
1308
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
1309
|
+
io_uring_prep_splice(sqe, in_fd, -1, out_fd, -1, 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_tee(struct um *machine, int in_fd, int out_fd, uint nbytes) {
|
|
1323
|
+
struct um_op *op = um_op_acquire(machine);
|
|
1324
|
+
um_prep_op(machine, op, OP_TEE, 2, 0);
|
|
1325
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
1326
|
+
io_uring_prep_tee(sqe, in_fd, out_fd, nbytes, 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
|
+
|
|
1339
|
+
VALUE um_fsync(struct um *machine, int fd) {
|
|
1340
|
+
struct um_op *op = um_op_acquire(machine);
|
|
1341
|
+
um_prep_op(machine, op, OP_FSYNC, 2, 0);
|
|
1342
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
1343
|
+
io_uring_prep_fsync(sqe, fd, 0);
|
|
1344
|
+
|
|
1345
|
+
VALUE ret = um_yield(machine);
|
|
1346
|
+
|
|
1347
|
+
if (likely(um_verify_op_completion(machine, op, false))) ret = INT2NUM(op->result.res);
|
|
1348
|
+
um_op_release(machine, op);
|
|
1349
|
+
|
|
1350
|
+
RAISE_IF_EXCEPTION(ret);
|
|
1351
|
+
RB_GC_GUARD(ret);
|
|
1352
|
+
|
|
1353
|
+
return ret;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1288
1356
|
/*******************************************************************************
|
|
1289
1357
|
multishot ops
|
|
1290
1358
|
*******************************************************************************/
|
|
@@ -1427,7 +1495,7 @@ int read_recv_each_multishot_process_result(struct op_ctx *ctx, struct um_op_res
|
|
|
1427
1495
|
return false;
|
|
1428
1496
|
|
|
1429
1497
|
*total += result->res;
|
|
1430
|
-
VALUE buf =
|
|
1498
|
+
VALUE buf = um_read_from_buffer_ring(ctx->machine, ctx->bgid, result->res, result->flags);
|
|
1431
1499
|
rb_yield(buf);
|
|
1432
1500
|
RB_GC_GUARD(buf);
|
|
1433
1501
|
|