uringmachine 1.0.1 → 1.0.4
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 +13 -0
- data/TODO.md +38 -15
- data/examples/irb_fiber.rb +14 -0
- data/ext/um/um.c +1 -1
- data/ext/um/um.h +2 -0
- data/ext/um/um_io.c +86 -40
- data/ext/um/um_io_class.c +39 -0
- data/ext/um/um_op.c +1 -2
- data/lib/uringmachine/dns_resolver.rb +1 -1
- data/lib/uringmachine/version.rb +1 -1
- data/test/test_io.rb +126 -0
- data/test/test_um.rb +52 -5
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dea49e5d6f13c7919ccaa9765428bfc81058ad1fab0b3f1df07d0fd60a5b1cd3
|
|
4
|
+
data.tar.gz: 3cd06ca52b40e891807b589bc7d7f97cfc67be2d491e5855a9624de959f160a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06ef57453cc0986a0b047b9d91ab5add06cf64145d5fd17ba5d85edf1b01f1bde0cea060d3a54bc87cadc9fc4c0137350ad704b0f0c1421f51c9ef9158eed8c5
|
|
7
|
+
data.tar.gz: 65a8701e871902c7e726cdaebacebef4bde5c111b6a3393b92d40a9b5a3005535f240cb30574f34fa41048206430b8faa41203d23a2fab603285c88eb9e766b4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# 1.0.4 2026-08-24
|
|
2
|
+
|
|
3
|
+
- Add `IO#read_uint_be` method
|
|
4
|
+
|
|
5
|
+
# 1.0.3 2026-08-24
|
|
6
|
+
|
|
7
|
+
- Add `IO#read_int_be` method
|
|
8
|
+
- Don't schedule fiber for async multishot ops (fixes potential segfault when using UM::IO)
|
|
9
|
+
|
|
10
|
+
# 1.0.2 2026-05-25
|
|
11
|
+
|
|
12
|
+
- Fix DNS resolver
|
|
13
|
+
|
|
1
14
|
# 1.0.1 2026-05-12
|
|
2
15
|
|
|
3
16
|
- Fix Net::HTTP performance regression when running on fiber scheduler
|
data/TODO.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
## immediate
|
|
2
2
|
|
|
3
|
+
- IO methods:
|
|
4
|
+
```ruby
|
|
5
|
+
io.read_int_be(length)
|
|
6
|
+
io.read_uint_be(length)
|
|
7
|
+
io.read_int_le(length)
|
|
8
|
+
io.read_uint_le(length)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- It might be interesting to have a DSL where we can define a frame/header structure:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
http2_transform = UM::IO.transform do
|
|
15
|
+
length uint(24)
|
|
16
|
+
type uint(8)
|
|
17
|
+
flags uint(8)
|
|
18
|
+
stream_id uint(32)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
frame = io.read(http2_transform)
|
|
22
|
+
raise if frame[:length] > MAX_LENGTH
|
|
23
|
+
raise if stream_id & 0x80000000
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- SSL kTLS:
|
|
27
|
+
- discussion: https://share.gemini.google/tPWiFpr3bWeF
|
|
28
|
+
- API:
|
|
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
|
+
|
|
3
41
|
- Add `IO#http_xxx` methods
|
|
4
42
|
- `#http_read_request_headers()`
|
|
5
43
|
- `#http_read_body(content_length)` (-1 means chunked TE)
|
|
@@ -84,21 +122,6 @@
|
|
|
84
122
|
- madvise
|
|
85
123
|
- getxattr / setxattr
|
|
86
124
|
|
|
87
|
-
## actors
|
|
88
|
-
|
|
89
|
-
When doing a `call`, we need to provide a mailbox for the response. can this be
|
|
90
|
-
automatic?
|
|
91
|
-
|
|
92
|
-
## Syntax / pattern for launching/supervising multiple operations
|
|
93
|
-
|
|
94
|
-
Select (see above):
|
|
95
|
-
|
|
96
|
-
```ruby
|
|
97
|
-
# select
|
|
98
|
-
machine.join_select(*fibers) #=> [result, fiber]
|
|
99
|
-
machine.shift_select(*queues) #=> [result, queue]
|
|
100
|
-
```
|
|
101
|
-
|
|
102
125
|
## Other abstractions
|
|
103
126
|
|
|
104
127
|
- Happy eyeballs connect
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
require 'uringmachine'
|
|
6
|
+
require 'uringmachine/fiber_scheduler'
|
|
7
|
+
require 'irb'
|
|
8
|
+
|
|
9
|
+
@machine = UringMachine.new
|
|
10
|
+
@scheduler = UM::FiberScheduler.new(@machine)
|
|
11
|
+
Fiber.set_scheduler @scheduler
|
|
12
|
+
|
|
13
|
+
f = @machine.spin { IRB.start }
|
|
14
|
+
@machine.join(f)
|
data/ext/um/um.c
CHANGED
|
@@ -192,7 +192,7 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
|
|
|
192
192
|
um_op_multishot_results_push(machine, op, cqe->res, cqe->flags);
|
|
193
193
|
|
|
194
194
|
op->flags |= done ? (OP_F_CQE_SEEN | OP_F_CQE_DONE) : OP_F_CQE_SEEN;
|
|
195
|
-
if (!
|
|
195
|
+
if (!(op->flags & (OP_F_SCHEDULED | OP_F_ASYNC))) um_schedule_op(machine, op);
|
|
196
196
|
}
|
|
197
197
|
else {
|
|
198
198
|
// single shot
|
data/ext/um/um.h
CHANGED
|
@@ -426,6 +426,8 @@ VALUE io_read(struct um_io *io, VALUE out_buffer, ssize_t len, size_t inc, int s
|
|
|
426
426
|
VALUE io_read_to_delim(struct um_io *io, VALUE out_buffer, VALUE delim, ssize_t maxlen);
|
|
427
427
|
void io_skip(struct um_io *io, size_t inc, int safe_inc);
|
|
428
428
|
void io_read_each(struct um_io *io);
|
|
429
|
+
VALUE io_read_int_be(struct um_io *io, size_t len);
|
|
430
|
+
VALUE io_read_uint_be(struct um_io *io, size_t len);
|
|
429
431
|
size_t io_write_raw(struct um_io *io, const char *buffer, size_t len);
|
|
430
432
|
VALUE io_writev(struct um_io *io, int argc, VALUE *argv);
|
|
431
433
|
VALUE resp_read(struct um_io *io, VALUE out_buffer);
|
data/ext/um/um_io.c
CHANGED
|
@@ -74,13 +74,12 @@ void um_io_cleanup(struct um_io *io) {
|
|
|
74
74
|
um_segment_checkin(io->machine, io->head);
|
|
75
75
|
io->head = next;
|
|
76
76
|
}
|
|
77
|
+
io->tail = NULL;
|
|
77
78
|
io->pending_bytes = 0;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
// returns true if case of ENOBUFS error, sets more to true if more data forthcoming
|
|
81
|
-
inline int io_process_segments(
|
|
82
|
-
struct um_io *io, size_t *total_bytes, int *more) {
|
|
83
|
-
|
|
82
|
+
inline int io_process_segments(struct um_io *io, size_t *total_bytes, int *more) {
|
|
84
83
|
*more = 0;
|
|
85
84
|
struct um_op_result *result = &io->op->result;
|
|
86
85
|
io->op->flags &= ~OP_F_CQE_SEEN;
|
|
@@ -125,6 +124,7 @@ void io_clear(struct um_io *io) {
|
|
|
125
124
|
um_segment_checkin(io->machine, io->head);
|
|
126
125
|
io->head = next;
|
|
127
126
|
}
|
|
127
|
+
io->tail = NULL;
|
|
128
128
|
io->pending_bytes = 0;
|
|
129
129
|
|
|
130
130
|
if (io->working_buffer) {
|
|
@@ -133,6 +133,30 @@ void io_clear(struct um_io *io) {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
inline void io_handle_enobufs(struct um_io *io) {
|
|
137
|
+
// we want to restart only if in case we don't already have lots of pending
|
|
138
|
+
// bytes in the stream. This heuristic might need a closer look to determine
|
|
139
|
+
// if this is the correct behavior.
|
|
140
|
+
int should_restart = io->pending_bytes < (io->machine->bp_buffer_size * 4);
|
|
141
|
+
|
|
142
|
+
// fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
|
|
143
|
+
// io,
|
|
144
|
+
// total_bytes, io->pending_bytes, io->machine->bp_commit_level,
|
|
145
|
+
// io->machine->bp_buffer_count,
|
|
146
|
+
// same_threshold, should_restart
|
|
147
|
+
// );
|
|
148
|
+
|
|
149
|
+
if (should_restart) {
|
|
150
|
+
// If multiple IO ops are happening at the same time, they'll all get
|
|
151
|
+
// ENOBUFS! We track the buffer pool commit level in the op in order to
|
|
152
|
+
// prevent running bp_handle_enobufs() more than once.
|
|
153
|
+
if (io->op->bp_commit_level == io->machine->bp_commit_level)
|
|
154
|
+
bp_handle_enobufs(io->machine);
|
|
155
|
+
}
|
|
156
|
+
um_op_release(io->machine, io->op);
|
|
157
|
+
io->op = NULL;
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
inline void io_await_segments(struct um_io *io) {
|
|
137
161
|
if (unlikely(!io->op)) io_multishot_op_start(io);
|
|
138
162
|
|
|
@@ -156,40 +180,12 @@ int io_get_more_segments_bp(struct um_io *io) {
|
|
|
156
180
|
io_await_segments(io);
|
|
157
181
|
enobufs = io_process_segments(io, &total_bytes, &more);
|
|
158
182
|
um_op_multishot_results_clear(io->machine, io->op);
|
|
159
|
-
if (unlikely(enobufs)) {
|
|
160
|
-
int should_restart = io->pending_bytes < (io->machine->bp_buffer_size * 4);
|
|
161
|
-
// int same_threshold = io->op->bp_commit_level == io->machine->bp_commit_level;
|
|
162
|
-
|
|
163
|
-
// fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
|
|
164
|
-
// io,
|
|
165
|
-
// total_bytes, io->pending_bytes, io->machine->bp_commit_level,
|
|
166
|
-
// io->machine->bp_buffer_count,
|
|
167
|
-
// same_threshold, should_restart
|
|
168
|
-
// );
|
|
169
|
-
|
|
170
|
-
// If multiple IO ops are happening at the same time, they'll all
|
|
171
|
-
// get ENOBUFS! We track the commit threshold in the op in order to
|
|
172
|
-
// prevent running bp_handle_enobufs() more than once.
|
|
173
|
-
|
|
174
|
-
if (should_restart) {
|
|
175
|
-
if (io->op->bp_commit_level == io->machine->bp_commit_level)
|
|
176
|
-
bp_handle_enobufs(io->machine);
|
|
177
|
-
|
|
178
|
-
um_op_release(io->machine, io->op);
|
|
179
|
-
io->op = NULL;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
um_op_release(io->machine, io->op);
|
|
183
|
-
io->op = NULL;
|
|
184
|
-
}
|
|
185
183
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
else
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (total_bytes || io->eof) return total_bytes;
|
|
192
|
-
}
|
|
184
|
+
if (unlikely(enobufs))
|
|
185
|
+
io_handle_enobufs(io);
|
|
186
|
+
else if (more)
|
|
187
|
+
io->op->flags &= ~OP_F_CQE_SEEN;
|
|
188
|
+
if (total_bytes || io->eof) return total_bytes;
|
|
193
189
|
}
|
|
194
190
|
}
|
|
195
191
|
|
|
@@ -254,9 +250,7 @@ inline void io_skip(struct um_io *io, size_t inc, int safe_inc) {
|
|
|
254
250
|
io->pending_bytes -= inc_len;
|
|
255
251
|
if (io->pos == io->head->len) {
|
|
256
252
|
io_shift_head(io);
|
|
257
|
-
if (inc && safe_inc && !io->head)
|
|
258
|
-
if (!io_get_more_segments(io)) break;
|
|
259
|
-
}
|
|
253
|
+
if (inc && safe_inc && !io->head && !io_get_more_segments(io)) break;
|
|
260
254
|
}
|
|
261
255
|
}
|
|
262
256
|
}
|
|
@@ -285,6 +279,58 @@ inline void io_read_each(struct um_io *io) {
|
|
|
285
279
|
RB_GC_GUARD(buffer);
|
|
286
280
|
}
|
|
287
281
|
|
|
282
|
+
VALUE io_read_int_be(struct um_io *io, size_t len) {
|
|
283
|
+
if (unlikely(io->eof && !io->head)) return Qnil;
|
|
284
|
+
if (!io->tail && !io_get_more_segments(io)) return Qnil;
|
|
285
|
+
|
|
286
|
+
long value = 0;
|
|
287
|
+
struct um_segment *current = io->head;
|
|
288
|
+
|
|
289
|
+
while (true) {
|
|
290
|
+
size_t segment_len = current->len - io->pos;
|
|
291
|
+
int pos = 0;
|
|
292
|
+
while (segment_len > 0 && len > 0) {
|
|
293
|
+
value = value * 0x100 + *(unsigned char *)(current->ptr + io->pos + pos);
|
|
294
|
+
len--;
|
|
295
|
+
segment_len--;
|
|
296
|
+
pos++;
|
|
297
|
+
}
|
|
298
|
+
if (pos > 0) io_skip(io, pos, true);
|
|
299
|
+
|
|
300
|
+
if (len == 0) return LONG2NUM(value);
|
|
301
|
+
|
|
302
|
+
if (!io->head && !io_get_more_segments(io))
|
|
303
|
+
return Qnil;
|
|
304
|
+
current = io->head;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
VALUE io_read_uint_be(struct um_io *io, size_t len) {
|
|
309
|
+
if (unlikely(io->eof && !io->head)) return Qnil;
|
|
310
|
+
if (!io->tail && !io_get_more_segments(io)) return Qnil;
|
|
311
|
+
|
|
312
|
+
unsigned long value = 0;
|
|
313
|
+
struct um_segment *current = io->head;
|
|
314
|
+
|
|
315
|
+
while (true) {
|
|
316
|
+
size_t segment_len = current->len - io->pos;
|
|
317
|
+
int pos = 0;
|
|
318
|
+
while (segment_len > 0 && len > 0) {
|
|
319
|
+
value = value * 0x100 + *(unsigned char *)(current->ptr + io->pos + pos);
|
|
320
|
+
len--;
|
|
321
|
+
segment_len--;
|
|
322
|
+
pos++;
|
|
323
|
+
}
|
|
324
|
+
if (pos > 0) io_skip(io, pos, true);
|
|
325
|
+
|
|
326
|
+
if (len == 0) return ULONG2NUM(value);
|
|
327
|
+
|
|
328
|
+
if (!io->head && !io_get_more_segments(io))
|
|
329
|
+
return Qnil;
|
|
330
|
+
current = io->head;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
288
334
|
inline void io_copy(struct um_io *io, char *dest, size_t len) {
|
|
289
335
|
while (len) {
|
|
290
336
|
char *segment_ptr = io->head->ptr + io->pos;
|
|
@@ -316,7 +362,7 @@ VALUE io_consume_string(struct um_io *io, VALUE out_buffer, size_t len, size_t i
|
|
|
316
362
|
char *dest = RSTRING_PTR(str);
|
|
317
363
|
|
|
318
364
|
io_copy(io, dest, len);
|
|
319
|
-
io_skip(io, inc, safe_inc);
|
|
365
|
+
if (inc) io_skip(io, inc, safe_inc);
|
|
320
366
|
return str;
|
|
321
367
|
RB_GC_GUARD(str);
|
|
322
368
|
}
|
data/ext/um/um_io_class.c
CHANGED
|
@@ -242,6 +242,42 @@ VALUE IO_read_each(VALUE self) {
|
|
|
242
242
|
return self;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
#define MAX_INT_SIZE 8
|
|
246
|
+
|
|
247
|
+
/* call-seq:
|
|
248
|
+
* conn.read_int_be(len) -> int
|
|
249
|
+
*
|
|
250
|
+
* Reads a signed integer from the target in big endian notation. The len
|
|
251
|
+
* parameter indicates the integer field length in bytes, with an upper limit of
|
|
252
|
+
* 8 bytes. If EOF is encountered before enough bytes are read, returns nil.
|
|
253
|
+
*
|
|
254
|
+
* @param len [Integer] number of bytes to read
|
|
255
|
+
* @return [Integer, nil] integer value or nil if EOF
|
|
256
|
+
*/
|
|
257
|
+
VALUE IO_read_int_be(VALUE self, VALUE len) {
|
|
258
|
+
struct um_io *conn = um_get_io(self);
|
|
259
|
+
size_t len_i = NUM2UINT(len);
|
|
260
|
+
if (unlikely(len_i > MAX_INT_SIZE)) rb_raise(eUMError, "Invalid length given");
|
|
261
|
+
return io_read_int_be(conn, len_i);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* call-seq:
|
|
265
|
+
* conn.read_uint_be(len) -> int
|
|
266
|
+
*
|
|
267
|
+
* Reads an unsigned integer from the target in big endian notation. The len
|
|
268
|
+
* parameter indicates the integer field length in bytes, with an upper limit
|
|
269
|
+
* of 8 bytes. If EOF is encountered before enough bytes are read, returns nil.
|
|
270
|
+
*
|
|
271
|
+
* @param len [Integer] number of bytes to read
|
|
272
|
+
* @return [Integer, nil] integer value or nil if EOF
|
|
273
|
+
*/
|
|
274
|
+
VALUE IO_read_uint_be(VALUE self, VALUE len) {
|
|
275
|
+
struct um_io *conn = um_get_io(self);
|
|
276
|
+
size_t len_i = NUM2UINT(len);
|
|
277
|
+
if (unlikely(len_i > MAX_INT_SIZE)) rb_raise(eUMError, "Invalid length given");
|
|
278
|
+
return io_read_uint_be(conn, len_i);
|
|
279
|
+
}
|
|
280
|
+
|
|
245
281
|
/* call-seq:
|
|
246
282
|
* conn.write(*bufs) -> len
|
|
247
283
|
*
|
|
@@ -375,6 +411,9 @@ void Init_IO(void) {
|
|
|
375
411
|
rb_define_method(cIO, "skip", IO_skip, 1);
|
|
376
412
|
rb_define_method(cIO, "read_each", IO_read_each, 0);
|
|
377
413
|
|
|
414
|
+
rb_define_method(cIO, "read_int_be", IO_read_int_be, 1);
|
|
415
|
+
rb_define_method(cIO, "read_uint_be", IO_read_uint_be, 1);
|
|
416
|
+
|
|
378
417
|
rb_define_method(cIO, "write", IO_write, -1);
|
|
379
418
|
|
|
380
419
|
rb_define_method(cIO, "resp_read", IO_resp_read, 0);
|
data/ext/um/um_op.c
CHANGED
|
@@ -156,9 +156,8 @@ inline void um_op_multishot_results_push(struct um *machine, struct um_op *op, _
|
|
|
156
156
|
result->res = res;
|
|
157
157
|
result->flags = flags;
|
|
158
158
|
result->next = NULL;
|
|
159
|
-
if (op->flags & OP_F_BUFFER_POOL && res >= 0)
|
|
159
|
+
if (op->flags & OP_F_BUFFER_POOL && res >= 0)
|
|
160
160
|
result->segment = bp_get_op_result_segment(machine, op, res, flags);
|
|
161
|
-
}
|
|
162
161
|
|
|
163
162
|
op->multishot_result_tail = result;
|
|
164
163
|
op->multishot_result_count++;
|
|
@@ -44,7 +44,7 @@ class UringMachine
|
|
|
44
44
|
# @return [Array<String>] name servers
|
|
45
45
|
def get_nameservers
|
|
46
46
|
nameservers = []
|
|
47
|
-
IO.readlines('/etc/resolv.conf').each do |line|
|
|
47
|
+
::IO.readlines('/etc/resolv.conf').each do |line|
|
|
48
48
|
if line =~ /^nameserver (.+)$/
|
|
49
49
|
nameservers << $1.split(/\s+/).first
|
|
50
50
|
end
|
data/lib/uringmachine/version.rb
CHANGED
data/test/test_io.rb
CHANGED
|
@@ -305,6 +305,22 @@ class IOTest < IOBaseTest
|
|
|
305
305
|
machine.terminate(f)
|
|
306
306
|
machine.join(f)
|
|
307
307
|
end
|
|
308
|
+
|
|
309
|
+
def test_io_read_segfault
|
|
310
|
+
machine.write(@wfd, "abcd")
|
|
311
|
+
conn.read(4)
|
|
312
|
+
|
|
313
|
+
machine.write(@wfd, "\x00\x01\x05\xdf")
|
|
314
|
+
conn.read(4)
|
|
315
|
+
|
|
316
|
+
f = machine.spin do
|
|
317
|
+
machine.write(@wfd, "\xde")
|
|
318
|
+
machine.snooze
|
|
319
|
+
machine.write(@wfd, "\x90\x03")
|
|
320
|
+
end
|
|
321
|
+
value = conn.read(3)
|
|
322
|
+
assert_equal "\xde\x90\x03".b, value
|
|
323
|
+
end
|
|
308
324
|
end
|
|
309
325
|
|
|
310
326
|
class IOWriteTest < UMBaseTest
|
|
@@ -806,3 +822,113 @@ class IOByteCountsTest < IOBaseTest
|
|
|
806
822
|
assert_equal 0, conn.pending
|
|
807
823
|
end
|
|
808
824
|
end
|
|
825
|
+
|
|
826
|
+
class IOIntTest < IOBaseTest
|
|
827
|
+
def test_io_read_int_be
|
|
828
|
+
machine.write(@wfd, "abcd")
|
|
829
|
+
conn.read(4)
|
|
830
|
+
|
|
831
|
+
machine.write(@wfd, "\x00\x01\x05\xdf")
|
|
832
|
+
value = conn.read_int_be(2)
|
|
833
|
+
assert_equal 0x0001, value
|
|
834
|
+
value = conn.read_int_be(2)
|
|
835
|
+
assert_equal 0x05df, value
|
|
836
|
+
|
|
837
|
+
machine.write(@wfd, "\x00\x01\x05\xdf")
|
|
838
|
+
value = conn.read_int_be(4)
|
|
839
|
+
assert_equal 0x000105df, value
|
|
840
|
+
|
|
841
|
+
machine.write(@wfd, "\xde\xfa")
|
|
842
|
+
machine.snooze
|
|
843
|
+
machine.write(@wfd, "\xdd\x03")
|
|
844
|
+
machine.snooze
|
|
845
|
+
value = conn.read_int_be(4)
|
|
846
|
+
assert_equal 0xdefadd03, value
|
|
847
|
+
|
|
848
|
+
machine.write(@wfd, "\xed\xcb\xa9")
|
|
849
|
+
value = conn.read_int_be(3)
|
|
850
|
+
assert_equal 0xedcba9, value
|
|
851
|
+
|
|
852
|
+
f = machine.spin do
|
|
853
|
+
machine.write(@wfd, "\xde")
|
|
854
|
+
machine.snooze
|
|
855
|
+
machine.write(@wfd, "\x90\x03")
|
|
856
|
+
end
|
|
857
|
+
value = conn.read_int_be(3)
|
|
858
|
+
machine.join(f)
|
|
859
|
+
assert_equal 0xde9003, value
|
|
860
|
+
|
|
861
|
+
machine.write(@wfd, "\xde\xfa")
|
|
862
|
+
machine.close(@wfd)
|
|
863
|
+
assert_nil conn.read_int_be(4)
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
def test_io_read_int_be_invalid_length
|
|
867
|
+
machine.write(@wfd, (1..8).to_a.pack('C*'))
|
|
868
|
+
assert_raises(UM::Error) { conn.read_int_be(-1) }
|
|
869
|
+
assert_raises(UM::Error) { conn.read_int_be(20) }
|
|
870
|
+
assert_raises(UM::Error) { conn.read_int_be(9) }
|
|
871
|
+
|
|
872
|
+
# 8 is my limit on Schnitzengruben
|
|
873
|
+
value = conn.read_int_be(8)
|
|
874
|
+
assert_equal 0x0102030405060708, value
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def test_io_read_uint_be
|
|
878
|
+
machine.write(@wfd, "abcd")
|
|
879
|
+
conn.read(4)
|
|
880
|
+
|
|
881
|
+
machine.write(@wfd, "\x00\x01\x05\xdf")
|
|
882
|
+
value = conn.read_uint_be(2)
|
|
883
|
+
assert_equal 0x0001, value
|
|
884
|
+
value = conn.read_uint_be(2)
|
|
885
|
+
assert_equal 0x05df, value
|
|
886
|
+
|
|
887
|
+
machine.write(@wfd, "\x00\x01\x05\xdf")
|
|
888
|
+
value = conn.read_uint_be(4)
|
|
889
|
+
assert_equal 0x000105df, value
|
|
890
|
+
|
|
891
|
+
machine.write(@wfd, "\xde\xfa")
|
|
892
|
+
machine.snooze
|
|
893
|
+
machine.write(@wfd, "\xdd\x03")
|
|
894
|
+
machine.snooze
|
|
895
|
+
value = conn.read_uint_be(4)
|
|
896
|
+
assert_equal 0xdefadd03, value
|
|
897
|
+
|
|
898
|
+
machine.write(@wfd, "\xed\xcb\xa9")
|
|
899
|
+
value = conn.read_uint_be(3)
|
|
900
|
+
assert_equal 0xedcba9, value
|
|
901
|
+
|
|
902
|
+
f = machine.spin do
|
|
903
|
+
machine.write(@wfd, "\xde")
|
|
904
|
+
machine.snooze
|
|
905
|
+
machine.write(@wfd, "\x90\x03")
|
|
906
|
+
end
|
|
907
|
+
value = conn.read_uint_be(3)
|
|
908
|
+
machine.join(f)
|
|
909
|
+
assert_equal 0xde9003, value
|
|
910
|
+
|
|
911
|
+
machine.write(@wfd, "\xff" * 8)
|
|
912
|
+
value = conn.read_uint_be(8)
|
|
913
|
+
assert_equal 0xffffffffffffffff, value
|
|
914
|
+
|
|
915
|
+
machine.write(@wfd, "\x80\x00\x00\x00\x00\x00\x00\x00")
|
|
916
|
+
value = conn.read_uint_be(8)
|
|
917
|
+
assert_equal 0x8000000000000000, value
|
|
918
|
+
|
|
919
|
+
machine.write(@wfd, "\xde\xfa")
|
|
920
|
+
machine.close(@wfd)
|
|
921
|
+
assert_nil conn.read_uint_be(4)
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
def test_io_read_uint_be_invalid_length
|
|
925
|
+
machine.write(@wfd, (0xf1..0xf8).to_a.pack('C*'))
|
|
926
|
+
assert_raises(UM::Error) { conn.read_uint_be(-1) }
|
|
927
|
+
assert_raises(UM::Error) { conn.read_uint_be(20) }
|
|
928
|
+
assert_raises(UM::Error) { conn.read_uint_be(9) }
|
|
929
|
+
|
|
930
|
+
# 8 is my limit on Schnitzengruben
|
|
931
|
+
value = conn.read_uint_be(8)
|
|
932
|
+
assert_equal 0xf1f2f3f4f5f6f7f8, value
|
|
933
|
+
end
|
|
934
|
+
end
|
data/test/test_um.rb
CHANGED
|
@@ -3277,6 +3277,23 @@ class FileWatchTest < UMBaseTest
|
|
|
3277
3277
|
name: 'foo.txt'
|
|
3278
3278
|
}
|
|
3279
3279
|
], events
|
|
3280
|
+
|
|
3281
|
+
fn = File.join(@root, '.bar')
|
|
3282
|
+
IO.write(fn, 'barbar')
|
|
3283
|
+
|
|
3284
|
+
events = machine.inotify_get_events(fd)
|
|
3285
|
+
assert_equal [
|
|
3286
|
+
{
|
|
3287
|
+
wd: wd,
|
|
3288
|
+
mask: UM::IN_CREATE,
|
|
3289
|
+
name: '.bar'
|
|
3290
|
+
},
|
|
3291
|
+
{
|
|
3292
|
+
wd: wd,
|
|
3293
|
+
mask: UM::IN_CLOSE_WRITE,
|
|
3294
|
+
name: '.bar'
|
|
3295
|
+
}
|
|
3296
|
+
], events
|
|
3280
3297
|
end
|
|
3281
3298
|
|
|
3282
3299
|
def write_file(fn, data)
|
|
@@ -3386,6 +3403,25 @@ class FileWatchTest < UMBaseTest
|
|
|
3386
3403
|
{ mask: UM::IN_CREATE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3387
3404
|
{ mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3388
3405
|
], events
|
|
3406
|
+
|
|
3407
|
+
fn = File.join(@root, 'bar/.dotfile')
|
|
3408
|
+
write_file(fn, 'dotdot')
|
|
3409
|
+
machine.sleep(0.05)
|
|
3410
|
+
assert_equal [
|
|
3411
|
+
{ mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'foo.txt') },
|
|
3412
|
+
{ mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'foo/baz.txt') },
|
|
3413
|
+
{ mask: UM::IN_CREATE | UM::IN_ISDIR, fn: File.join(@root, 'bar') },
|
|
3414
|
+
{ mask: UM::IN_CREATE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3415
|
+
{ mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3416
|
+
{ mask: UM::IN_DELETE, fn: File.join(@root, 'foo/baz.txt') },
|
|
3417
|
+
{ mask: UM::IN_DELETE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3418
|
+
{ mask: UM::IN_DELETE | UM::IN_ISDIR, fn: File.join(@root, 'bar') },
|
|
3419
|
+
{ mask: UM::IN_CREATE | UM::IN_ISDIR, fn: File.join(@root, 'bar') },
|
|
3420
|
+
{ mask: UM::IN_CREATE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3421
|
+
{ mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/baz.txt') },
|
|
3422
|
+
{ mask: UM::IN_CREATE, fn: File.join(@root, 'bar/.dotfile') },
|
|
3423
|
+
{ mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/.dotfile') },
|
|
3424
|
+
], events
|
|
3389
3425
|
ensure
|
|
3390
3426
|
machine.schedule(f, UM::Terminate.new)
|
|
3391
3427
|
machine.join(f)
|
|
@@ -3397,22 +3433,26 @@ class SetChildSubreaperTest < Minitest::Test
|
|
|
3397
3433
|
r, w = IO.pipe
|
|
3398
3434
|
UM.pr_set_child_subreaper(true)
|
|
3399
3435
|
|
|
3400
|
-
child_pid = fork
|
|
3436
|
+
child_pid = fork do
|
|
3401
3437
|
r2, w2 = IO.pipe
|
|
3402
|
-
pid = fork
|
|
3438
|
+
pid = fork do
|
|
3403
3439
|
r.close
|
|
3404
3440
|
w.close
|
|
3405
3441
|
w2.close
|
|
3406
3442
|
r2.read
|
|
3407
3443
|
r2.close
|
|
3408
3444
|
sleep(0.01)
|
|
3409
|
-
|
|
3445
|
+
ensure
|
|
3446
|
+
exit!
|
|
3447
|
+
end
|
|
3410
3448
|
w << pid
|
|
3411
3449
|
w.close
|
|
3412
3450
|
r2.close
|
|
3413
3451
|
w2 << 'done'
|
|
3414
3452
|
w2.close
|
|
3415
|
-
|
|
3453
|
+
ensure
|
|
3454
|
+
exit!
|
|
3455
|
+
end
|
|
3416
3456
|
Process.wait(child_pid)
|
|
3417
3457
|
|
|
3418
3458
|
w.close
|
|
@@ -3635,4 +3675,11 @@ class FsyncTest < UMBaseTest
|
|
|
3635
3675
|
def test_fsync_bad_args
|
|
3636
3676
|
assert_raises(Errno::EINVAL) { machine.fsync(1) }
|
|
3637
3677
|
end
|
|
3638
|
-
end
|
|
3678
|
+
end
|
|
3679
|
+
|
|
3680
|
+
class ResolveTest < UMBaseTest
|
|
3681
|
+
def test_resolve_ipv4
|
|
3682
|
+
ip = machine.resolve('localhost')
|
|
3683
|
+
assert_equal ['127.0.0.1'], ip
|
|
3684
|
+
end
|
|
3685
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: uringmachine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
@@ -103,6 +103,7 @@ files:
|
|
|
103
103
|
- examples/http_server.rb
|
|
104
104
|
- examples/inout.rb
|
|
105
105
|
- examples/io_uring_simple.c
|
|
106
|
+
- examples/irb_fiber.rb
|
|
106
107
|
- examples/nc.rb
|
|
107
108
|
- examples/nc_ssl.rb
|
|
108
109
|
- examples/pg.rb
|
|
@@ -505,7 +506,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
505
506
|
- !ruby/object:Gem::Version
|
|
506
507
|
version: '0'
|
|
507
508
|
requirements: []
|
|
508
|
-
rubygems_version: 4.0.
|
|
509
|
+
rubygems_version: 4.0.6
|
|
509
510
|
specification_version: 4
|
|
510
511
|
summary: A lean, mean io_uring machine
|
|
511
512
|
test_files: []
|