uringmachine 1.0.4 → 1.0.6
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 +8 -0
- data/TODO.md +53 -53
- data/ext/um/um.h +1 -0
- data/ext/um/um_io.c +47 -3
- data/ext/um/um_io_class.c +12 -6
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +14 -2
- data/test/test_io.rb +18 -0
- data/test/test_um.rb +18 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c37bb6e6971036cefa78d76efbb4425f843087b4b4f5e24fe16e695920746b8
|
|
4
|
+
data.tar.gz: 144e109b5dfa199adda69387c7229c78393f3ee8b0a45f545db76934f896ef2c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d19ae0d9c4056c691a8b9c93901f759e2b149ebd275d8a5d1e0dcfc33e2308eef0e583541b2ce90b69d55efc20b4ab0c7415f4e44d0c9753b07e733bc965e90
|
|
7
|
+
data.tar.gz: 697653d6d9120bcbece655bf530e1e87e9fc461e2052bd20b8666f344440ee11898b569c3d98a7477c39dd11bdf6ff03fc6b1e08f953c257a84fb0aea58a882f
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
|
@@ -1,4 +1,46 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Character scanning in UM::IO
|
|
2
|
+
|
|
3
|
+
- Refactor character scanning
|
|
4
|
+
- functions/macros for iterating over segments
|
|
5
|
+
|
|
6
|
+
- SIMD?
|
|
7
|
+
- prep work for SIMD character scanning
|
|
8
|
+
- we'll probably need a bit of helper code for dealing with SIMD
|
|
9
|
+
- a good overview here: https://mitchellh.com/writing/everyone-should-know-simd
|
|
10
|
+
- SIMD prep work should be done once before iterating over segments
|
|
11
|
+
|
|
12
|
+
```c
|
|
13
|
+
// bitmaps for character types can be generated with a bit of Ruby:
|
|
14
|
+
//
|
|
15
|
+
// def t(r); (0..255).map { [it].pack('c') =~ r ? 1 : 0 }; end
|
|
16
|
+
// def tn(r); (0..255).map { [it].pack('c') =~ r ? 0 : 1 }; end
|
|
17
|
+
// def u64(bits); bits.reverse.join.to_i(2); end
|
|
18
|
+
// def p(a); a.each_slice(64).map { u64(it) }; end
|
|
19
|
+
|
|
20
|
+
// usage:
|
|
21
|
+
//
|
|
22
|
+
// p(t(/[a-zA-Z0-9]/)).map { format('%016X', it) }
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// /[a-zA-Z0-9]/
|
|
26
|
+
uint64_t alpha_numeric[] = [
|
|
27
|
+
0x000000000000FFC0,
|
|
28
|
+
0x7FFFFFE07FFFFFE0,
|
|
29
|
+
0x0000000000000000,
|
|
30
|
+
0x0000000000000000
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// HTTP method: /[a-zA-Z]/ (3-12 characters)
|
|
34
|
+
// header-key: /[a-zA-Z\-]/ ()
|
|
35
|
+
// path: /^($/
|
|
36
|
+
|
|
37
|
+
// check if character is in bitmap
|
|
38
|
+
inline int test_char(char c, uint64 *bitmap) {
|
|
39
|
+
return bitmap[c / 64] & (1UL << (c % 64));
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## IO methods
|
|
2
44
|
|
|
3
45
|
- IO methods:
|
|
4
46
|
```ruby
|
|
@@ -23,30 +65,20 @@
|
|
|
23
65
|
raise if stream_id & 0x80000000
|
|
24
66
|
```
|
|
25
67
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
```ruby
|
|
31
|
-
machine.ssl_setup_ktls(fd)
|
|
32
|
-
|
|
33
|
-
# then we can just use normal send/recv
|
|
34
|
-
machine.send(fd, "foo!")
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
- SSL custom BIO:
|
|
38
|
-
- release GVL before calling `SSL_read`, `SSL_write`
|
|
39
|
-
- reacquire GVL (`rb_thread_call_with_gvl`) in `um_bio_read`, `um_bio_write`
|
|
40
|
-
|
|
41
|
-
- Add `IO#http_xxx` methods
|
|
42
|
-
- `#http_read_request_headers()`
|
|
43
|
-
- `#http_read_body(content_length)` (-1 means chunked TE)
|
|
68
|
+
- Add `IO#http1_xxx` methods
|
|
69
|
+
- `#http1_read_request_headers()`
|
|
70
|
+
- `#http1_read_body(content_length)` (-1 means chunked TE)
|
|
44
71
|
|
|
45
72
|
- Add tests for support for Set in `machine#await`
|
|
46
73
|
- Add tests for support for Set, Array in `machine#join`
|
|
47
|
-
- Add `UM#read_file` for reading entire file
|
|
74
|
+
- Add `UM#read_file` for reading entire file
|
|
48
75
|
- Add `UM#write_file` for writing entire file
|
|
49
76
|
|
|
77
|
+
## SSL custom BIO:
|
|
78
|
+
|
|
79
|
+
- release GVL before calling `SSL_read`, `SSL_write`
|
|
80
|
+
- reacquire GVL (`rb_thread_call_with_gvl`) in `um_bio_read`, `um_bio_write`
|
|
81
|
+
|
|
50
82
|
## Balancing I/O with the runqueue
|
|
51
83
|
|
|
52
84
|
- in some cases where there are many entries in the runqueue, this can
|
|
@@ -109,6 +141,7 @@
|
|
|
109
141
|
- select on fibers:
|
|
110
142
|
- select fibers that are done
|
|
111
143
|
- select first done fiber
|
|
144
|
+
- we might want to rename `#select` to `#fdselect`
|
|
112
145
|
|
|
113
146
|
## ops still not implemented
|
|
114
147
|
|
|
@@ -131,36 +164,3 @@
|
|
|
131
164
|
# ['1.1.1.1:80', '2.2.2.2:80']
|
|
132
165
|
tcp_connect_he(*addrs)
|
|
133
166
|
```
|
|
134
|
-
|
|
135
|
-
## Character scanning in UM::IO
|
|
136
|
-
|
|
137
|
-
```c
|
|
138
|
-
// bitmaps for character types can be generated with a bit of Ruby:
|
|
139
|
-
//
|
|
140
|
-
// def t(r); (0..255).map { [it].pack('c') =~ r ? 1 : 0 }; end
|
|
141
|
-
// def tn(r); (0..255).map { [it].pack('c') =~ r ? 0 : 1 }; end
|
|
142
|
-
// def u64(bits); bits.reverse.join.to_i(2); end
|
|
143
|
-
// def p(a); a.each_slice(64).map { u64(it) }; end
|
|
144
|
-
|
|
145
|
-
// usage:
|
|
146
|
-
//
|
|
147
|
-
// p(t(/[a-zA-Z0-9]/)).map { format('%016X', it) }
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// /[a-zA-Z0-9]/
|
|
151
|
-
uint64_t alpha_numeric[] = [
|
|
152
|
-
0x000000000000FFC0,
|
|
153
|
-
0x7FFFFFE07FFFFFE0,
|
|
154
|
-
0x0000000000000000,
|
|
155
|
-
0x0000000000000000
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
// HTTP method: /[a-zA-Z]/ (3-12 characters)
|
|
159
|
-
// header-key: /[a-zA-Z\-]/ ()
|
|
160
|
-
// path: /^($/
|
|
161
|
-
|
|
162
|
-
// check if character is in bitmap
|
|
163
|
-
inline int test_char(char c, uint64 *bitmap) {
|
|
164
|
-
return bitmap[c / 64] & (1UL << (c % 64));
|
|
165
|
-
}
|
|
166
|
-
```
|
data/ext/um/um.h
CHANGED
data/ext/um/um_io.c
CHANGED
|
@@ -157,7 +157,7 @@ inline void io_handle_enobufs(struct um_io *io) {
|
|
|
157
157
|
io->op = NULL;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
inline void io_await_segments(struct um_io *io) {
|
|
160
|
+
static inline void io_await_segments(struct um_io *io) {
|
|
161
161
|
if (unlikely(!io->op)) io_multishot_op_start(io);
|
|
162
162
|
|
|
163
163
|
if (!OP_CQE_SEEN_P(io->op)) {
|
|
@@ -208,11 +208,54 @@ int io_get_more_segments_ssl(struct um_io *io) {
|
|
|
208
208
|
return 1;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
int io_get_more_segments_file(struct um_io *io) {
|
|
212
|
+
if (!io->working_buffer)
|
|
213
|
+
io->working_buffer = bp_buffer_checkout(io->machine);
|
|
214
|
+
|
|
215
|
+
char *ptr = io->working_buffer->buf + io->working_buffer->pos;
|
|
216
|
+
size_t maxlen = io->working_buffer->len - io->working_buffer->pos;
|
|
217
|
+
|
|
218
|
+
struct um_op *op = um_op_acquire(io->machine);
|
|
219
|
+
um_prep_op(io->machine, op, OP_READ, 2, 0);
|
|
220
|
+
struct io_uring_sqe *sqe = um_get_sqe(io->machine, op);
|
|
221
|
+
io_uring_prep_read(sqe, io->fd, ptr, maxlen, -1);
|
|
222
|
+
|
|
223
|
+
VALUE ret = um_yield(io->machine);
|
|
224
|
+
int has_more = 0;
|
|
225
|
+
|
|
226
|
+
if (likely(um_verify_op_completion(io->machine, op, true))) {
|
|
227
|
+
int res = op->result.res;
|
|
228
|
+
|
|
229
|
+
if (res == 0) {
|
|
230
|
+
bp_buffer_checkin(io->machine, io->working_buffer);
|
|
231
|
+
io->working_buffer = NULL;
|
|
232
|
+
goto done;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
struct um_segment *segment = bp_buffer_consume(io->machine, io->working_buffer, res);
|
|
236
|
+
io_add_segment(io, segment);
|
|
237
|
+
|
|
238
|
+
if ((size_t)res == maxlen) {
|
|
239
|
+
bp_buffer_checkin(io->machine, io->working_buffer);
|
|
240
|
+
io->working_buffer = NULL;
|
|
241
|
+
}
|
|
242
|
+
else has_more = 1;
|
|
243
|
+
}
|
|
244
|
+
um_op_release(io->machine, op);
|
|
245
|
+
|
|
246
|
+
done:
|
|
247
|
+
RAISE_IF_EXCEPTION(ret);
|
|
248
|
+
RB_GC_GUARD(ret);
|
|
249
|
+
return has_more;
|
|
250
|
+
}
|
|
251
|
+
|
|
211
252
|
int io_get_more_segments(struct um_io *io) {
|
|
212
253
|
switch (io->mode) {
|
|
213
254
|
case IO_FD:
|
|
214
255
|
case IO_SOCKET:
|
|
215
256
|
return io_get_more_segments_bp(io);
|
|
257
|
+
case IO_FILE:
|
|
258
|
+
return io_get_more_segments_file(io);
|
|
216
259
|
case IO_SSL:
|
|
217
260
|
return io_get_more_segments_ssl(io);
|
|
218
261
|
default:
|
|
@@ -438,14 +481,15 @@ VALUE io_read(struct um_io *io, VALUE out_buffer, ssize_t len, size_t inc, int s
|
|
|
438
481
|
|
|
439
482
|
while (true) {
|
|
440
483
|
size_t segment_len = current->len - pos;
|
|
441
|
-
if (abs_len && segment_len > remaining_len) {
|
|
484
|
+
if (abs_len && segment_len > (size_t)remaining_len) {
|
|
442
485
|
segment_len = remaining_len;
|
|
443
486
|
}
|
|
444
487
|
total_len += segment_len;
|
|
445
488
|
if (abs_len) {
|
|
446
489
|
remaining_len -= segment_len;
|
|
447
|
-
if (!remaining_len)
|
|
490
|
+
if (!remaining_len) {
|
|
448
491
|
return io_consume_string(io, out_buffer, total_len, inc, safe_inc);
|
|
492
|
+
}
|
|
449
493
|
}
|
|
450
494
|
|
|
451
495
|
if (!current->next) {
|
data/ext/um/um_io_class.c
CHANGED
|
@@ -4,6 +4,7 @@ VALUE cIO;
|
|
|
4
4
|
VALUE eIORESPError;
|
|
5
5
|
|
|
6
6
|
VALUE SYM_fd;
|
|
7
|
+
VALUE SYM_file;
|
|
7
8
|
VALUE SYM_socket;
|
|
8
9
|
VALUE SYM_ssl;
|
|
9
10
|
|
|
@@ -87,6 +88,7 @@ static inline void io_set_target(struct um_io *conn, VALUE target, enum um_io_mo
|
|
|
87
88
|
conn->mode = mode;
|
|
88
89
|
switch (mode) {
|
|
89
90
|
case IO_FD:
|
|
91
|
+
case IO_FILE:
|
|
90
92
|
case IO_SOCKET:
|
|
91
93
|
conn->fd = NUM2INT(target);
|
|
92
94
|
return;
|
|
@@ -109,6 +111,8 @@ static inline void io_setup(struct um_io *conn, VALUE target, VALUE mode) {
|
|
|
109
111
|
}
|
|
110
112
|
else if (mode == SYM_fd)
|
|
111
113
|
io_set_target(conn, target, IO_FD);
|
|
114
|
+
else if (mode == SYM_file)
|
|
115
|
+
io_set_target(conn, target, IO_FILE);
|
|
112
116
|
else if (mode == SYM_socket)
|
|
113
117
|
io_set_target(conn, target, IO_SOCKET);
|
|
114
118
|
else if (mode == SYM_ssl)
|
|
@@ -157,10 +161,11 @@ VALUE IO_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
157
161
|
VALUE IO_mode(VALUE self) {
|
|
158
162
|
struct um_io *conn = um_get_io(self);
|
|
159
163
|
switch (conn->mode) {
|
|
160
|
-
case IO_FD:
|
|
161
|
-
case
|
|
162
|
-
case
|
|
163
|
-
|
|
164
|
+
case IO_FD: return SYM_fd;
|
|
165
|
+
case IO_FILE: return SYM_file;
|
|
166
|
+
case IO_SOCKET: return SYM_socket;
|
|
167
|
+
case IO_SSL: return SYM_ssl;
|
|
168
|
+
default: return Qnil;
|
|
164
169
|
}
|
|
165
170
|
return Qnil;
|
|
166
171
|
}
|
|
@@ -427,7 +432,8 @@ void Init_IO(void) {
|
|
|
427
432
|
|
|
428
433
|
eIORESPError = rb_define_class_under(cIO, "RESPError", rb_eStandardError);
|
|
429
434
|
|
|
430
|
-
SYM_fd
|
|
431
|
-
|
|
435
|
+
SYM_fd = ID2SYM(rb_intern("fd"));
|
|
436
|
+
SYM_file = ID2SYM(rb_intern("file"));
|
|
437
|
+
SYM_socket = ID2SYM(rb_intern("socket"));
|
|
432
438
|
SYM_ssl = ID2SYM(rb_intern("ssl"));
|
|
433
439
|
}
|
data/lib/uringmachine/version.rb
CHANGED
data/lib/uringmachine.rb
CHANGED
|
@@ -50,7 +50,7 @@ class UringMachine
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Runs the given block in the given fiber. This method is used to run fibers
|
|
53
|
-
# indirectly.
|
|
53
|
+
# indirectly. See FiberScheduler#fiber
|
|
54
54
|
#
|
|
55
55
|
# @param fiber [Fiber] fiber
|
|
56
56
|
# @param block [Proc] block to run
|
|
@@ -202,9 +202,10 @@ class UringMachine
|
|
|
202
202
|
# the type of target and how it is read from:
|
|
203
203
|
#
|
|
204
204
|
# - :fd - read from the given fd using the buffer pool (default mode)
|
|
205
|
+
# - :file - read from a regular file
|
|
205
206
|
# - :socket - receive from the given socket fd using the buffer pool
|
|
206
207
|
# - :ssl - read from the given SSL socket
|
|
207
|
-
#
|
|
208
|
+
#
|
|
208
209
|
# If a block is given, the block will be called with the IO instance as
|
|
209
210
|
# argument and the method will return the block's return value.
|
|
210
211
|
#
|
|
@@ -220,6 +221,17 @@ class UringMachine
|
|
|
220
221
|
res
|
|
221
222
|
end
|
|
222
223
|
|
|
224
|
+
# call-seq:
|
|
225
|
+
# machine.file_read(fn) -> data
|
|
226
|
+
#
|
|
227
|
+
# Reads and returns the content of the given filename, with a maximum length of 1GB.
|
|
228
|
+
#
|
|
229
|
+
# @param fn [String] filename
|
|
230
|
+
# @return [String] file content
|
|
231
|
+
def file_read(fn)
|
|
232
|
+
open(fn, O_RDONLY) { io(it, :file).read(-(1 << 30)) }
|
|
233
|
+
end
|
|
234
|
+
|
|
223
235
|
# Creates, binds and sets up a TCP socket for listening on the given host and
|
|
224
236
|
# port.
|
|
225
237
|
#
|
data/test/test_io.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative 'helper'
|
|
|
4
4
|
require 'securerandom'
|
|
5
5
|
require 'openssl'
|
|
6
6
|
require 'localhost/authority'
|
|
7
|
+
require 'tempfile'
|
|
7
8
|
|
|
8
9
|
class IOBaseTest < UMBaseTest
|
|
9
10
|
attr_reader :conn
|
|
@@ -715,6 +716,23 @@ class IOModeTest < UMBaseTest
|
|
|
715
716
|
sock2&.close rescue nil
|
|
716
717
|
end
|
|
717
718
|
|
|
719
|
+
def test_io_file_mode
|
|
720
|
+
fn = Tempfile.new('uringmachine_test_io_from_file').path
|
|
721
|
+
IO.write(fn, (0..9).to_a.join)
|
|
722
|
+
fd = machine.open(fn, UM::O_RDONLY)
|
|
723
|
+
io = machine.io(fd, :file)
|
|
724
|
+
buf = io.read(3)
|
|
725
|
+
assert_equal '012', buf
|
|
726
|
+
|
|
727
|
+
buf = io.read(5)
|
|
728
|
+
assert_equal '34567', buf
|
|
729
|
+
|
|
730
|
+
buf = io.read(-4)
|
|
731
|
+
assert_equal '89', buf
|
|
732
|
+
ensure
|
|
733
|
+
machine.close(fd) if fd
|
|
734
|
+
end
|
|
735
|
+
|
|
718
736
|
def test_io_socket_mode_non_socket
|
|
719
737
|
r, w = UM.pipe
|
|
720
738
|
machine.write(w, 'foobar')
|
data/test/test_um.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'helper'
|
|
4
4
|
require 'socket'
|
|
5
|
+
require 'tempfile'
|
|
5
6
|
|
|
6
7
|
class UringMachineTest < Minitest::Test
|
|
7
8
|
def test_kernel_version
|
|
@@ -3520,6 +3521,23 @@ class IOMethodTest < UMBaseTest
|
|
|
3520
3521
|
end
|
|
3521
3522
|
end
|
|
3522
3523
|
|
|
3524
|
+
class FileHelperMethodsTest < UMBaseTest
|
|
3525
|
+
def setup
|
|
3526
|
+
super
|
|
3527
|
+
@fn = Tempfile.new('uringmachine_test_io_from_file').path
|
|
3528
|
+
end
|
|
3529
|
+
|
|
3530
|
+
def test_file_read
|
|
3531
|
+
IO.write(@fn, 'foobar' * 321)
|
|
3532
|
+
buf = machine.file_read(@fn)
|
|
3533
|
+
assert_equal 'foobar' * 321, buf
|
|
3534
|
+
end
|
|
3535
|
+
|
|
3536
|
+
def test_file_read_bad_fn
|
|
3537
|
+
assert_raises(Errno::ENOENT) { machine.file_read(@fn + '-missing') }
|
|
3538
|
+
end
|
|
3539
|
+
end
|
|
3540
|
+
|
|
3523
3541
|
class TCPHelperMethodsTest < UMBaseTest
|
|
3524
3542
|
def setup
|
|
3525
3543
|
super
|