Skip to content

Commit b4196f6

Browse files
committed
Rename UM::Connection to UM::IO
1 parent fe733e4 commit b4196f6

20 files changed

Lines changed: 492 additions & 491 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.32.0 2026-04-03
2+
3+
- Rename `Connection` to `IO`
4+
15
# 0.31.0 2026-03-31
26

37
- Rework `Stream` into `Connection` class:

README.md

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ implementation that allows integration with the entire Ruby ecosystem.
3737
- Excellent performance characteristics for concurrent I/O-bound applications.
3838
- `Fiber::Scheduler` implementation to automatically integrate with the Ruby
3939
ecosystem in a transparent fashion.
40-
- [Connection](#connections) class with automatic buffer management for reading.
40+
- [IO](#io-api) class with automatic buffer management for reading.
4141
- Optimized I/O for encrypted SSL connections.
4242

4343
## Design
@@ -286,70 +286,69 @@ fiber = Fiber.schedule do
286286
end
287287
```
288288

289-
## Connections
289+
## IO API
290290

291-
`UringMachine::Connection` is a class designed for efficiently read from and
292-
write to a socket or other file descriptor. Connections are ideal for
293-
implementing the read side of protocols, and provide an API that is useful for
294-
both line-based protocols and binary (frame-based) protocols.
291+
`UringMachine::IO` is a class designed for efficiently read from and write to a
292+
socket or other file descriptor. The IO class is ideal for implementing
293+
line-based and binary (frame-based) protocols.
295294

296-
A connection is associated with a UringMachine instance and a target file
297-
descriptor (or SSL socket, see also [connection modes](#connection-modes)
298-
below). Behind the scenes, connections take advantage of io_uring's registered
299-
buffers feature, and more recently, the introduction of [incremental buffer
295+
An IO is associated with a UringMachine instance and a target file descriptor
296+
(or SSL socket, see also [IO modes](#io-modes) below). Behind the scenes, the IO
297+
class takes advantage of io_uring's provided buffers feature, and more recently,
298+
the introduction of [incremental buffer
300299
consumption](https://github.com/axboe/liburing/wiki/What's-new-with-io_uring-in-6.11-and-6.12#incremental-provided-buffer-consumption).
301300

302-
When connections are used, UringMachine automatically manages the buffers it
301+
When IO instances are used, UringMachine automatically manages the buffers it
303302
provides to the kernel, maximizing buffer reuse and minimizing allocations.
304303
UringMachine also responds to stress conditions (increased incoming traffic) by
305304
automatically provisioning additional buffers.
306305

307-
To create a connection for a given fd, use `UM#connection`:
306+
To create an IO for a given fd, use `UM#io`:
308307

309308
```ruby
310-
conn = machine.connection(fd)
309+
io = machine.io(fd)
311310

312-
# you can also provide a block that will be passed the connection instance:
313-
machine.connection(fd) { |c| do_something_with(c) }
311+
# you can provide a block that will be passed the IO instance:
312+
machine.io(fd) { |io| do_something_with(io) }
314313

315-
# you can also instantiate a connection directly:
316-
conn = UM::Connection.new(machine, fd)
314+
# you can also instantiate an IO directly:
315+
io = UM::IO.new(machine, fd)
317316
```
318317

319-
The following API is used to interact with the connection:
318+
The following API is used to interact with an IO:
320319

321320
```ruby
322321
# Read until a newline character is encountered:
323-
line = conn.read_line(0)
322+
line = io.read_line(0)
324323

325324
# Read line with a maximum length of 13 bytes:
326-
line = conn.read_line(13)
325+
line = io.read_line(13)
327326

328327
# Read all data:
329-
buf = conn.read(0)
328+
buf = io.read(0)
330329

331330
# Read exactly 13 bytes:
332-
buf = conn.read(13)
331+
buf = io.read(13)
333332

334333
# Read up to 13 bytes:
335-
buf = conn.read(-13)
334+
buf = io.read(-13)
336335

337336
# Read continuously until EOF
338-
conn.read_each { |data| ... }
337+
io.read_each { |data| ... }
339338

340339
# Skip 3 bytes:
341-
conn.skip(3)
340+
io.skip(3)
342341

343342
# Write
344-
conn.write('foo', 'bar', 'baz')
343+
io.write('foo', 'bar', 'baz')
345344
```
346345

347346
Here's an example of a how a basic HTTP request parser might be implemented
348-
using a connection:
347+
using a `UM::IO`:
349348

350349
```ruby
351-
def parse_http_request_headers(conn)
352-
request_line = conn.read_line(0)
350+
def parse_http_request_headers(io)
351+
request_line = io.read_line(0)
353352
m = request_line.match(REQUEST_LINE_RE)
354353
return nil if !m
355354

@@ -360,7 +359,7 @@ def parse_http_request_headers(conn)
360359
}
361360

362361
while true
363-
line = conn.read_line(0)
362+
line = io.read_line(0)
364363
break if !line || line.empty?
365364

366365
m = line.match(HEADER_RE)
@@ -370,26 +369,26 @@ def parse_http_request_headers(conn)
370369
end
371370
```
372371

373-
### Connection modes
372+
### IO modes
374373

375-
Connection modes allow connections to be transport agnostic. Currently
376-
connections support three modes:
374+
IO modes allow IOs to be transport agnostic. The following modes are currently
375+
supported:
377376

378377
- `:fd` - use the buffer pool, read data using multishot read
379378
(this is the default mode).
380379
- `:socket` - use the buffer pool, read data using multishot recv.
381380
- `:ssl` - read from an `SSLSocket` object.
382381

383-
The mode is specified as an additional argument to `Connection.new`:
382+
The mode is specified as an additional argument to `IO.new`:
384383

385384
```ruby
386385
# using recv/send:
387-
conn = machine.connection(fd, :socket)
386+
io = machine.io(fd, :socket)
388387

389388
# SSL I/O:
390-
conn = machine.connection(ssl, :ssl)
389+
io = machine.io(ssl, :ssl)
391390
# or simply:
392-
conn = machine.connection(ssl)
391+
io = machine.io(ssl)
393392
```
394393

395394
## Performance

benchmark/gets.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ def um_read
3434
end
3535
end
3636

37-
@fd_connection = @machine.open('/dev/random', UM::O_RDONLY)
38-
@conn = UM::Connection.new(@machine, @fd_connection)
39-
def um_connection_read_line
40-
@conn.read_line(0)
37+
@fd_io = @machine.open('/dev/random', UM::O_RDONLY)
38+
@io = UM::IO.new(@machine, @fd_io)
39+
def um_io_read_line
40+
@io².read_line(0)
4141
end
4242

4343
Benchmark.ips do |x|
44-
x.report('IO#gets') { io_gets }
45-
x.report('UM#read+buf') { um_read }
46-
x.report('UM::Connection') { um_connection_read_line }
44+
x.report('IO#gets') { io_gets }
45+
x.report('UM#read+buf') { um_read }
46+
x.report('UM::IO') { um_io_read_line }
4747

4848
x.compare!(order: :baseline)
4949
end

benchmark/gets_concurrent.rb

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,40 +83,40 @@ def um_read
8383
stop_server
8484
end
8585

86-
@total_connection = 0
87-
def um_connection_do
86+
@total_io = 0
87+
def um_io_do
8888
# fd = @machine.open('/dev/random', UM::O_RDONLY)
8989
fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
9090
@machine.connect(fd, '127.0.0.1', 1234)
91-
conn = UM::Connection.new(@machine, fd)
92-
N.times { @total_connection += conn.read_line(0)&.bytesize || 0 }
91+
io = UM::IO.new(@machine, fd)
92+
N.times { @total_io += io.read_line(0)&.bytesize || 0 }
9393
rescue => e
9494
p e
9595
p e.backtrace
9696
ensure
97-
conn.clear
97+
io.clear
9898
@machine.close(fd)
9999
end
100100

101-
def um_connection
101+
def um_io
102102
start_server
103103
ff = C.times.map {
104104
@machine.snooze
105-
@machine.spin { um_connection_do }
105+
@machine.spin { um_io_do }
106106
}
107107
@machine.await(ff)
108-
pp total: @total_connection
108+
pp total: @total_io
109109
ensure
110110
stop_server
111111
end
112112

113113
p(C:, N:)
114-
um_connection
114+
um_io
115115
pp @machine.metrics
116116
exit
117117

118118
Benchmark.bm do
119-
it.report('Thread/IO#gets') { io_gets }
120-
it.report('Fiber/UM#read+buf') { um_read }
121-
it.report('Fiber/UM::Stream') { um_connection }
119+
it.report('Thread/IO#gets') { io_gets }
120+
it.report('Fiber/UM#read+buf') { um_read }
121+
it.report('Fiber/UM::IO') { um_io }
122122
end

benchmark/http_parse.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ def parse_http_stringio
129129
($machine.close(wfd) rescue nil) if wfd
130130
end
131131

132-
def connection_parse_headers(fd)
133-
conn = UM::Connection.new($machine, fd)
132+
def io_parse_headers(fd)
133+
io = UM::IO.new($machine, fd)
134134

135135
buf = String.new(capacity: 65536)
136-
headers = connection_get_request_line(conn, buf)
136+
headers = io_get_request_line(io, buf)
137137
return nil if !headers
138138

139139
while true
140-
line = conn.read_line(0)
140+
line = io.read_line(0)
141141
break if line.empty?
142142

143143
m = line.match(RE_HEADER_LINE)
@@ -149,8 +149,8 @@ def connection_parse_headers(fd)
149149
headers
150150
end
151151

152-
def connection_get_request_line(conn, buf)
153-
line = conn.read_line(0)
152+
def io_get_request_line(io, buf)
153+
line = io.read_line(0)
154154

155155
m = line.match(RE_REQUEST_LINE)
156156
return nil if !m
@@ -162,12 +162,12 @@ def connection_get_request_line(conn, buf)
162162
}
163163
end
164164

165-
def parse_http_connection
165+
def parse_http_io
166166
rfd, wfd = UM.pipe
167167
queue = UM::Queue.new
168168

169169
$machine.spin do
170-
headers = connection_parse_headers(rfd)
170+
headers = io_parse_headers(rfd)
171171
$machine.push(queue, headers)
172172
rescue Exception => e
173173
p e
@@ -188,7 +188,7 @@ def compare_allocs
188188
p(
189189
alloc_http_parser: alloc_count { x.times { parse_http_parser } },
190190
alloc_stringio: alloc_count { x.times { parse_http_stringio } },
191-
alloc_connection: alloc_count { x.times { parse_http_connection } }
191+
alloc_io: alloc_count { x.times { parse_http_io } }
192192
)
193193
ensure
194194
GC.enable
@@ -213,8 +213,8 @@ def benchmark
213213
x.config(:time => 5, :warmup => 3)
214214

215215
x.report("http_parser") { parse_http_parser }
216-
x.report("stringio") { parse_http_stringio }
217-
x.report("connection") { parse_http_connection }
216+
x.report("StringIO") { parse_http_stringio }
217+
x.report("UM::IO") { parse_http_io }
218218

219219
x.compare!
220220
end

benchmark/http_server_accept_queue.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
1313
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
1414

15-
def connection_get_request_line(conn, buf)
16-
line = conn.read_line(0)
15+
def io_get_request_line(io, buf)
16+
line = io.read_line(0)
1717
m = line&.match(RE_REQUEST_LINE)
1818
return nil if !m
1919

@@ -26,12 +26,12 @@ def connection_get_request_line(conn, buf)
2626

2727
class InvalidHeadersError < StandardError; end
2828

29-
def get_headers(conn, buf)
30-
headers = connection_get_request_line(conn, buf)
29+
def get_headers(io, buf)
30+
headers = io_get_request_line(io, buf)
3131
return nil if !headers
3232

3333
while true
34-
line = conn.read_line(0)
34+
line = io.read_line(0)
3535
break if line.empty?
3636

3737
m = line.match(RE_HEADER_LINE)
@@ -51,11 +51,11 @@ def send_response(machine, fd)
5151
end
5252

5353
def handle_connection(machine, fd)
54-
conn = UM::Connection.new(machine, fd)
54+
io = UM::IO.new(machine, fd)
5555
buf = String.new(capacity: 65536)
5656

5757
while true
58-
headers = get_headers(conn, buf)
58+
headers = get_headers(io, buf)
5959
break if !headers
6060

6161
send_response(machine, fd)

benchmark/http_server_multi_accept.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
1313
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
1414

15-
def connection_get_request_line(conn, buf)
16-
line = conn.read_line(0)
15+
def io_get_request_line(io, buf)
16+
line = io.read_line(0)
1717
m = line&.match(RE_REQUEST_LINE)
1818
return nil if !m
1919

@@ -26,12 +26,12 @@ def connection_get_request_line(conn, buf)
2626

2727
class InvalidHeadersError < StandardError; end
2828

29-
def get_headers(conn, buf)
30-
headers = connection_get_request_line(conn, buf)
29+
def get_headers(io, buf)
30+
headers = io_get_request_line(io, buf)
3131
return nil if !headers
3232

3333
while true
34-
line = conn.read_line(0)
34+
line = io.read_line(0)
3535
break if line.empty?
3636

3737
m = line.match(RE_HEADER_LINE)
@@ -51,11 +51,11 @@ def send_response(machine, fd)
5151
end
5252

5353
def handle_connection(machine, fd)
54-
conn = UM::Connection.new(machine, fd)
54+
io = UM::IO.new(machine, fd)
5555
buf = String.new(capacity: 65536)
5656

5757
while true
58-
headers = get_headers(conn, buf)
58+
headers = get_headers(io, buf)
5959
break if !headers
6060

6161
send_response(machine, fd)

0 commit comments

Comments
 (0)