uringmachine 0.29.0 → 0.29.1
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 +5 -0
- data/benchmark/gets_concurrent.rb +122 -0
- data/ext/um/um.c +8 -3
- data/ext/um/um.h +5 -7
- data/ext/um/um_buffer_pool.c +8 -6
- data/ext/um/um_class.c +4 -4
- data/ext/um/um_stream.c +62 -60
- data/grant-2025/tasks.md +3 -2
- data/lib/uringmachine/version.rb +1 -1
- data/test/helper.rb +2 -0
- data/test/test_fiber_scheduler.rb +3 -3
- data/test/test_stream.rb +31 -11
- data/test/test_um.rb +14 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 248a2f0e2a780904f26f0183d08237039e18b76b736e7b3563c2699e30041d65
|
|
4
|
+
data.tar.gz: 2020b0c8cc9be25becddef80fa4c66f0bc49e30d0791ec2b2b2e618fbf3ab0b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 543a8adf2c628f080f9ec249e11ff7e4bd969f0eca9e47285ba0ef20c28575ddd6d1902a3890d49ccb1ccf5100d13cd721bbd03c472bde15205344524fd7795c
|
|
7
|
+
data.tar.gz: 4afbe860f29187507c57a8e319d02f2f843d27aa7d65176f0e0040914bf4d8e68ce894d4663b13d3ace530bf2aa4fe3ca547e58d5cbcb1daf5d89f6d5bb94ae2
|
data/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
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
|
+
end
|
|
10
|
+
|
|
11
|
+
require 'benchmark'
|
|
12
|
+
require 'uringmachine'
|
|
13
|
+
|
|
14
|
+
C = 10
|
|
15
|
+
N = 1000
|
|
16
|
+
|
|
17
|
+
CMD = <<~EOF
|
|
18
|
+
bash -c "for i in {1..#{C*2}}; do nc -l -p 1234 </dev/random & done; wait $(jobs -p)"
|
|
19
|
+
EOF
|
|
20
|
+
|
|
21
|
+
def start_server
|
|
22
|
+
@pid = fork {
|
|
23
|
+
p :server_launch
|
|
24
|
+
`#{CMD}`
|
|
25
|
+
puts
|
|
26
|
+
p :server_done
|
|
27
|
+
puts
|
|
28
|
+
}
|
|
29
|
+
sleep(0.5)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def stop_server
|
|
33
|
+
Process.kill('SIGINT', @pid)
|
|
34
|
+
Process.wait(@pid)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def io_gets
|
|
38
|
+
start_server
|
|
39
|
+
tt = C.times.map {
|
|
40
|
+
Thread.new do
|
|
41
|
+
s = TCPSocket.new('localhost', 1234)
|
|
42
|
+
# io = File.open('/dev/random', 'r')
|
|
43
|
+
N.times { s.gets }
|
|
44
|
+
ensure
|
|
45
|
+
s.close
|
|
46
|
+
end
|
|
47
|
+
}
|
|
48
|
+
tt.each(&:join)
|
|
49
|
+
ensure
|
|
50
|
+
stop_server
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@machine = UM.new
|
|
54
|
+
|
|
55
|
+
def buf_gets(fd, buffer)
|
|
56
|
+
while true
|
|
57
|
+
idx = buffer.byteindex("\n")
|
|
58
|
+
if idx
|
|
59
|
+
line = buffer[0..(idx - 1)]
|
|
60
|
+
|
|
61
|
+
buffer = buffer[(idx + 1)..-1]
|
|
62
|
+
return line
|
|
63
|
+
end
|
|
64
|
+
@machine.read(fd, buffer, 65536, -1)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def um_read
|
|
69
|
+
start_server
|
|
70
|
+
ff = C.times.map {
|
|
71
|
+
@machine.spin do
|
|
72
|
+
# fd = @machine.open('/dev/random', UM::O_RDONLY)
|
|
73
|
+
fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
74
|
+
@machine.connect(fd, '127.0.0.1', 1234)
|
|
75
|
+
buffer = +''.encode(Encoding::US_ASCII)
|
|
76
|
+
N.times { buf_gets(fd, buffer) }
|
|
77
|
+
ensure
|
|
78
|
+
@machine.close(fd)
|
|
79
|
+
end
|
|
80
|
+
}
|
|
81
|
+
@machine.await(ff)
|
|
82
|
+
ensure
|
|
83
|
+
stop_server
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
@total_stream = 0
|
|
87
|
+
def um_stream_do
|
|
88
|
+
# fd = @machine.open('/dev/random', UM::O_RDONLY)
|
|
89
|
+
fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
90
|
+
@machine.connect(fd, '127.0.0.1', 1234)
|
|
91
|
+
stream = UM::Stream.new(@machine, fd)
|
|
92
|
+
N.times { @total_stream += stream.get_line(0)&.bytesize || 0 }
|
|
93
|
+
rescue => e
|
|
94
|
+
p e
|
|
95
|
+
p e.backtrace
|
|
96
|
+
ensure
|
|
97
|
+
stream.clear
|
|
98
|
+
@machine.close(fd)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def um_stream
|
|
102
|
+
start_server
|
|
103
|
+
ff = C.times.map {
|
|
104
|
+
@machine.snooze
|
|
105
|
+
@machine.spin { um_stream_do }
|
|
106
|
+
}
|
|
107
|
+
@machine.await(ff)
|
|
108
|
+
pp total: @total_stream
|
|
109
|
+
ensure
|
|
110
|
+
stop_server
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
p(C:, N:)
|
|
114
|
+
um_stream
|
|
115
|
+
pp @machine.metrics
|
|
116
|
+
exit
|
|
117
|
+
|
|
118
|
+
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_stream }
|
|
122
|
+
end
|
data/ext/um/um.c
CHANGED
|
@@ -540,15 +540,19 @@ VALUE um_timeout_complete(VALUE arg) {
|
|
|
540
540
|
return Qnil;
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
-
VALUE um_timeout(struct um *machine, VALUE interval, VALUE
|
|
543
|
+
VALUE um_timeout(struct um *machine, VALUE interval, VALUE obj) {
|
|
544
544
|
static ID ID_new = 0;
|
|
545
|
-
|
|
545
|
+
|
|
546
|
+
if (TYPE(obj) == T_CLASS) {
|
|
547
|
+
if (unlikely(!ID_new)) ID_new = rb_intern("new");
|
|
548
|
+
obj = rb_funcall(obj, ID_new, 0);
|
|
549
|
+
}
|
|
546
550
|
|
|
547
551
|
struct um_op *op = um_op_acquire(machine);
|
|
548
552
|
um_prep_op(machine, op, OP_TIMEOUT, 2, 0);
|
|
549
553
|
op->ts = um_double_to_timespec(NUM2DBL(interval));
|
|
550
554
|
RB_OBJ_WRITE(machine->self, &op->fiber, rb_fiber_current());
|
|
551
|
-
RB_OBJ_WRITE(machine->self, &op->value,
|
|
555
|
+
RB_OBJ_WRITE(machine->self, &op->value, obj);
|
|
552
556
|
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
553
557
|
|
|
554
558
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
@@ -556,6 +560,7 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
|
|
|
556
560
|
|
|
557
561
|
struct op_ctx ctx = { .machine = machine, .op = op };
|
|
558
562
|
return rb_ensure(rb_yield, Qnil, um_timeout_complete, (VALUE)&ctx);
|
|
563
|
+
RB_GC_GUARD(obj);
|
|
559
564
|
}
|
|
560
565
|
|
|
561
566
|
/*******************************************************************************
|
data/ext/um/um.h
CHANGED
|
@@ -111,12 +111,10 @@ enum um_stream_mode {
|
|
|
111
111
|
|
|
112
112
|
#define BP_BGID 0xF00B
|
|
113
113
|
#define BP_BR_ENTRIES 1024
|
|
114
|
-
|
|
115
114
|
#define BP_INITIAL_BUFFER_SIZE (1U << 14) // 16KB
|
|
116
|
-
#define
|
|
115
|
+
#define BP_INITIAL_COMMIT_LEVEL (BP_INITIAL_BUFFER_SIZE * 16) // 256KB
|
|
117
116
|
#define BP_MAX_BUFFER_SIZE (1U << 20) // 1MB
|
|
118
|
-
|
|
119
|
-
#define BP_MAX_COMMIT_THRESHOLD (BP_MAX_BUFFER_SIZE * BP_BR_ENTRIES) // 1GB
|
|
117
|
+
#define BP_MAX_COMMIT_LEVEL (BP_MAX_BUFFER_SIZE * BP_BR_ENTRIES) // 1GB
|
|
120
118
|
#define BP_AVAIL_BID_BITMAP_WORDS (BP_BR_ENTRIES / 64)
|
|
121
119
|
|
|
122
120
|
struct um_buffer {
|
|
@@ -163,7 +161,7 @@ struct um_op {
|
|
|
163
161
|
struct iovec *iovecs; // used for vectorized write/send
|
|
164
162
|
siginfo_t siginfo; // used for waitid
|
|
165
163
|
int int_value; // used for getsockopt
|
|
166
|
-
size_t
|
|
164
|
+
size_t bp_commit_level; // buffer pool commit threshold
|
|
167
165
|
};
|
|
168
166
|
};
|
|
169
167
|
|
|
@@ -234,7 +232,7 @@ struct um {
|
|
|
234
232
|
|
|
235
233
|
struct io_uring_buf_ring *bp_br;
|
|
236
234
|
size_t bp_buffer_size;
|
|
237
|
-
size_t
|
|
235
|
+
size_t bp_commit_level;
|
|
238
236
|
struct um_buffer **bp_commited_buffers;
|
|
239
237
|
uint64_t bp_avail_bid_bitmap[BP_AVAIL_BID_BITMAP_WORDS];
|
|
240
238
|
|
|
@@ -437,7 +435,7 @@ void stream_teardown(struct um_stream *stream);
|
|
|
437
435
|
void stream_clear(struct um_stream *stream);
|
|
438
436
|
VALUE stream_get_line(struct um_stream *stream, VALUE buf, size_t maxlen);
|
|
439
437
|
VALUE stream_get_string(struct um_stream *stream, VALUE out_buffer, ssize_t len, size_t inc, int safe_inc);
|
|
440
|
-
|
|
438
|
+
void stream_skip(struct um_stream *stream, size_t inc, int safe_inc);
|
|
441
439
|
VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
|
|
442
440
|
void resp_encode(struct um_write_buffer *buf, VALUE obj);
|
|
443
441
|
void resp_encode_cmd(struct um_write_buffer *buf, int argc, VALUE *argv);
|
data/ext/um/um_buffer_pool.c
CHANGED
|
@@ -31,7 +31,6 @@ inline struct um_buffer *bp_buffer_checkout(struct um *machine) {
|
|
|
31
31
|
buffer->ref_count++;
|
|
32
32
|
buffer->pos = 0;
|
|
33
33
|
buffer->next = NULL;
|
|
34
|
-
|
|
35
34
|
return buffer;
|
|
36
35
|
}
|
|
37
36
|
|
|
@@ -76,7 +75,7 @@ inline void bp_setup(struct um *machine) {
|
|
|
76
75
|
if (unlikely(!machine->bp_br)) rb_syserr_fail(ret, strerror(ret));
|
|
77
76
|
|
|
78
77
|
machine->bp_buffer_size = BP_INITIAL_BUFFER_SIZE;
|
|
79
|
-
machine->
|
|
78
|
+
machine->bp_commit_level = BP_INITIAL_COMMIT_LEVEL;
|
|
80
79
|
machine->bp_commited_buffers = malloc(sizeof(struct um_buffer) * BP_BR_ENTRIES);
|
|
81
80
|
memset(machine->bp_commited_buffers, 0, sizeof(struct um_buffer) * BP_BR_ENTRIES);
|
|
82
81
|
memset(machine->bp_avail_bid_bitmap, 0xFF, sizeof(machine->bp_avail_bid_bitmap));
|
|
@@ -160,10 +159,13 @@ inline struct um_buffer *get_buffer(struct um *machine, int bid) {
|
|
|
160
159
|
|
|
161
160
|
inline int should_commit_more_p(struct um *machine) {
|
|
162
161
|
return (machine->bp_buffer_count < BP_BR_ENTRIES) &&
|
|
163
|
-
(machine->bp_total_commited < machine->
|
|
162
|
+
(machine->bp_total_commited < machine->bp_commit_level);
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
inline void bp_ensure_commit_level(struct um *machine) {
|
|
166
|
+
if (machine->bp_total_commited > (machine->bp_commit_level / 2))
|
|
167
|
+
return;
|
|
168
|
+
|
|
167
169
|
int added = 0;
|
|
168
170
|
while (should_commit_more_p(machine)) {
|
|
169
171
|
if (likely(commit_buffer(machine, added))) added++;
|
|
@@ -178,11 +180,11 @@ inline void bp_ensure_commit_level(struct um *machine) {
|
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
inline void bp_handle_enobufs(struct um *machine) {
|
|
181
|
-
if (unlikely(machine->
|
|
183
|
+
if (unlikely(machine->bp_commit_level >= BP_MAX_COMMIT_LEVEL))
|
|
182
184
|
rb_raise(eUMError, "Buffer starvation");
|
|
183
185
|
|
|
184
|
-
machine->
|
|
185
|
-
while (machine->bp_buffer_size < machine->
|
|
186
|
+
machine->bp_commit_level *= 2;
|
|
187
|
+
while (machine->bp_buffer_size < machine->bp_commit_level / 4)
|
|
186
188
|
machine->bp_buffer_size *= 2;
|
|
187
189
|
bp_discard_buffer_freelist(machine);
|
|
188
190
|
}
|
data/ext/um/um_class.c
CHANGED
|
@@ -334,17 +334,17 @@ VALUE UM_schedule(VALUE self, VALUE fiber, VALUE value) {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
/* Runs the given block, interrupting its execution if its runtime exceeds the
|
|
337
|
-
* given timeout interval (in seconds).
|
|
337
|
+
* given timeout interval (in seconds), raising the specified exception.
|
|
338
338
|
*
|
|
339
339
|
* - https://www.man7.org/linux/man-pages//man3/io_uring_prep_timeoute.3.html
|
|
340
340
|
*
|
|
341
341
|
* @param interval [Number] timeout interval in seconds
|
|
342
|
-
* @param
|
|
342
|
+
* @param exception [any] timeout exception class or instance
|
|
343
343
|
* @return [any] block's return value
|
|
344
344
|
*/
|
|
345
|
-
VALUE UM_timeout(VALUE self, VALUE interval, VALUE
|
|
345
|
+
VALUE UM_timeout(VALUE self, VALUE interval, VALUE exception) {
|
|
346
346
|
struct um *machine = um_get_machine(self);
|
|
347
|
-
return um_timeout(machine, interval,
|
|
347
|
+
return um_timeout(machine, interval, exception);
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
/* Puts the current fiber to sleep for the given time duration (in seconds),
|
data/ext/um/um_stream.c
CHANGED
|
@@ -50,7 +50,7 @@ void stream_multishot_op_start(struct um_stream *stream) {
|
|
|
50
50
|
default:
|
|
51
51
|
um_raise_internal_error("Invalid multishot op");
|
|
52
52
|
}
|
|
53
|
-
stream->op->
|
|
53
|
+
stream->op->bp_commit_level = stream->machine->bp_commit_level;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
void stream_multishot_op_stop(struct um_stream *stream) {
|
|
@@ -157,15 +157,26 @@ int stream_get_more_segments_bp(struct um_stream *stream) {
|
|
|
157
157
|
um_op_multishot_results_clear(stream->machine, stream->op);
|
|
158
158
|
if (unlikely(enobufs)) {
|
|
159
159
|
int should_restart = stream->pending_len < (stream->machine->bp_buffer_size * 4);
|
|
160
|
+
// int same_threshold = stream->op->bp_commit_level == stream->machine->bp_commit_level;
|
|
161
|
+
|
|
162
|
+
// fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
|
|
163
|
+
// stream,
|
|
164
|
+
// total_bytes, stream->pending_len, stream->machine->bp_commit_level,
|
|
165
|
+
// stream->machine->bp_buffer_count,
|
|
166
|
+
// same_threshold, should_restart
|
|
167
|
+
// );
|
|
160
168
|
|
|
161
169
|
// If multiple stream ops are happening at the same time, they'll all get
|
|
162
170
|
// ENOBUFS! We track the commit threshold in the op in order to prevent
|
|
163
171
|
// running bp_handle_enobufs() more than once.
|
|
164
172
|
|
|
165
173
|
if (should_restart) {
|
|
166
|
-
if (stream->op->
|
|
174
|
+
if (stream->op->bp_commit_level == stream->machine->bp_commit_level)
|
|
167
175
|
bp_handle_enobufs(stream->machine);
|
|
168
|
-
|
|
176
|
+
|
|
177
|
+
um_op_release(stream->machine, stream->op);
|
|
178
|
+
stream->op = NULL;
|
|
179
|
+
// stream_multishot_op_start(stream);
|
|
169
180
|
}
|
|
170
181
|
else {
|
|
171
182
|
um_op_release(stream->machine, stream->op);
|
|
@@ -215,6 +226,44 @@ int stream_get_more_segments(struct um_stream *stream) {
|
|
|
215
226
|
|
|
216
227
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
217
228
|
|
|
229
|
+
inline void stream_shift_head(struct um_stream *stream) {
|
|
230
|
+
struct um_segment *consumed = stream->head;
|
|
231
|
+
stream->head = consumed->next;
|
|
232
|
+
if (!stream->head) stream->tail = NULL;
|
|
233
|
+
um_segment_checkin(stream->machine, consumed);
|
|
234
|
+
stream->pos = 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
inline void stream_skip(struct um_stream *stream, size_t inc, int safe_inc) {
|
|
238
|
+
while (inc) {
|
|
239
|
+
size_t segment_len = stream->head->len - stream->pos;
|
|
240
|
+
size_t inc_len = (segment_len <= inc) ? segment_len : inc;
|
|
241
|
+
inc -= inc_len;
|
|
242
|
+
stream->pos += inc_len;
|
|
243
|
+
stream->pending_len -= inc_len;
|
|
244
|
+
if (stream->pos == stream->head->len) {
|
|
245
|
+
stream_shift_head(stream);
|
|
246
|
+
if (inc && safe_inc && !stream->head) {
|
|
247
|
+
if (!stream_get_more_segments(stream)) break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
inline void stream_copy(struct um_stream *stream, char *dest, size_t len) {
|
|
254
|
+
while (len) {
|
|
255
|
+
char *segment_ptr = stream->head->ptr + stream->pos;
|
|
256
|
+
size_t segment_len = stream->head->len - stream->pos;
|
|
257
|
+
size_t cpy_len = (segment_len <= len) ? segment_len : len;
|
|
258
|
+
memcpy(dest, segment_ptr, cpy_len);
|
|
259
|
+
|
|
260
|
+
len -= cpy_len;
|
|
261
|
+
stream->pos += cpy_len;
|
|
262
|
+
stream->pending_len -= cpy_len;
|
|
263
|
+
dest += cpy_len;
|
|
264
|
+
if (stream->pos == stream->head->len) stream_shift_head(stream);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
218
267
|
|
|
219
268
|
VALUE stream_consume_string(struct um_stream *stream, VALUE out_buffer, size_t len, size_t inc, int safe_inc) {
|
|
220
269
|
VALUE str = Qnil;
|
|
@@ -228,69 +277,22 @@ VALUE stream_consume_string(struct um_stream *stream, VALUE out_buffer, size_t l
|
|
|
228
277
|
}
|
|
229
278
|
else
|
|
230
279
|
str = rb_str_new(NULL, len);
|
|
231
|
-
char *
|
|
232
|
-
while (len) {
|
|
233
|
-
char *segment_ptr = stream->head->ptr + stream->pos;
|
|
234
|
-
size_t segment_len = stream->head->len - stream->pos;
|
|
235
|
-
size_t cpy_len = (segment_len <= len) ? segment_len : len;
|
|
236
|
-
memcpy(str_ptr, segment_ptr, cpy_len);
|
|
280
|
+
char *dest = RSTRING_PTR(str);
|
|
237
281
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
stream->pending_len -= cpy_len;
|
|
241
|
-
str_ptr += cpy_len;
|
|
242
|
-
if (stream->pos == stream->head->len) {
|
|
243
|
-
struct um_segment *consumed = stream->head;
|
|
244
|
-
stream->head = consumed->next;
|
|
245
|
-
if (!stream->head) stream->tail = NULL;
|
|
246
|
-
um_segment_checkin(stream->machine, consumed);
|
|
247
|
-
stream->pos = 0;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
while (inc) {
|
|
252
|
-
size_t segment_len = stream->head->len - stream->pos;
|
|
253
|
-
size_t inc_len = (segment_len <= inc) ? segment_len : inc;
|
|
254
|
-
inc -= inc_len;
|
|
255
|
-
stream->pos += inc_len;
|
|
256
|
-
stream->pending_len -= inc_len;
|
|
257
|
-
if (stream->pos == stream->head->len) {
|
|
258
|
-
struct um_segment *consumed = stream->head;
|
|
259
|
-
stream->head = consumed->next;
|
|
260
|
-
um_segment_checkin(stream->machine, consumed);
|
|
261
|
-
if (!stream->head) {
|
|
262
|
-
stream->tail = NULL;
|
|
263
|
-
if (inc && safe_inc) {
|
|
264
|
-
if (!stream_get_more_segments(stream)) break;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
stream->pos = 0;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
282
|
+
stream_copy(stream, dest, len);
|
|
283
|
+
stream_skip(stream, inc, safe_inc);
|
|
270
284
|
return str;
|
|
271
285
|
RB_GC_GUARD(str);
|
|
272
286
|
}
|
|
273
287
|
|
|
274
|
-
// inline void stream_advance(struct um_stream *stream, size_t inc) {
|
|
275
|
-
// while (inc) {
|
|
276
|
-
// size_t segment_len = stream->head->len - stream->pos;
|
|
277
|
-
// size_t inc_len = (segment_len <= inc) ? segment_len : inc;
|
|
278
|
-
// inc -= inc_len;
|
|
279
|
-
// stream->pos += inc_len;
|
|
280
|
-
// if (stream->pos == stream->head->len) {
|
|
281
|
-
// struct um_segment *consumed = stream->head;
|
|
282
|
-
// stream->head = consumed->next;
|
|
283
|
-
// um_segment_checkin(stream->machine, consumed);
|
|
284
|
-
// if (!stream->head) {
|
|
285
|
-
// stream->tail = NULL;
|
|
286
|
-
// if (!stream_get_more_segments(stream)) return;
|
|
287
|
-
// }
|
|
288
|
-
// stream->pos = 0;
|
|
289
|
-
// }
|
|
290
|
-
// }
|
|
291
|
-
// }
|
|
292
|
-
|
|
293
288
|
VALUE stream_get_line(struct um_stream *stream, VALUE out_buffer, size_t maxlen) {
|
|
289
|
+
// if (stream->head) {
|
|
290
|
+
// fprintf(stderr, "head: %p pos: %ld: %.*s\n",
|
|
291
|
+
// stream->head, stream->pos,
|
|
292
|
+
// (int)(stream->head->len - stream->pos), stream->head->ptr + stream->pos
|
|
293
|
+
// );
|
|
294
|
+
// }
|
|
295
|
+
|
|
294
296
|
if (unlikely(stream->eof && !stream->head)) return Qnil;
|
|
295
297
|
if (!stream->tail && !stream_get_more_segments(stream)) return Qnil;
|
|
296
298
|
|
data/grant-2025/tasks.md
CHANGED
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
- [v] Data processing through a rewritten stream implementation.
|
|
36
36
|
|
|
37
37
|
- [ ] More benchmarks
|
|
38
|
-
- Different reading methods in concurrent setting
|
|
39
|
-
- SSL vs SSL-custom-BIO vs SSL-stream
|
|
38
|
+
- [ ] Different reading methods in concurrent setting
|
|
39
|
+
- [ ] SSL vs SSL-custom-BIO vs SSL-stream in concurrent setting
|
|
40
|
+
- [ ] stream resp client vs redis gem in concurrent setting
|
|
40
41
|
|
|
41
42
|
- [v] Sidecar mode
|
|
42
43
|
- [v] Convert `UM#initialize` to take kwargs
|
data/lib/uringmachine/version.rb
CHANGED
data/test/helper.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'securerandom'
|
|
|
6
6
|
require 'socket'
|
|
7
7
|
require 'net/http'
|
|
8
8
|
require 'json'
|
|
9
|
+
require 'timeout'
|
|
9
10
|
|
|
10
11
|
class MethodCallAuditor
|
|
11
12
|
attr_reader :calls
|
|
@@ -722,12 +723,11 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
722
723
|
sleep 1
|
|
723
724
|
end
|
|
724
725
|
res = true
|
|
725
|
-
rescue =>
|
|
726
|
-
res = e
|
|
726
|
+
rescue => res
|
|
727
727
|
end
|
|
728
728
|
@scheduler.join
|
|
729
|
-
assert_equal 3, machine.metrics[:total_ops]
|
|
730
729
|
assert_kind_of Timeout::Error, res
|
|
730
|
+
assert_equal 3, machine.metrics[:total_ops]
|
|
731
731
|
assert_equal({
|
|
732
732
|
fiber: 1,
|
|
733
733
|
timeout_after: 1,
|
data/test/test_stream.rb
CHANGED
|
@@ -46,7 +46,10 @@ class StreamTest < StreamBaseTest
|
|
|
46
46
|
assert stream.eof?
|
|
47
47
|
|
|
48
48
|
stream.clear
|
|
49
|
-
|
|
49
|
+
|
|
50
|
+
# initial buffer size: 6BKV, initial buffers commited: 16 (256KB)
|
|
51
|
+
# (plus an additional buffer commited after first usage)
|
|
52
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 6], buffer_metrics
|
|
50
53
|
assert_equal 0, machine.metrics[:ops_pending]
|
|
51
54
|
end
|
|
52
55
|
|
|
@@ -68,7 +71,7 @@ class StreamTest < StreamBaseTest
|
|
|
68
71
|
assert_equal 0, machine.metrics[:ops_pending]
|
|
69
72
|
assert_equal 256, machine.metrics[:segments_free]
|
|
70
73
|
|
|
71
|
-
assert_equal [
|
|
74
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 6], buffer_metrics
|
|
72
75
|
ensure
|
|
73
76
|
machine.close(rfd) rescue nil
|
|
74
77
|
machine.close(wfd) rescue nil
|
|
@@ -118,11 +121,11 @@ class StreamTest < StreamBaseTest
|
|
|
118
121
|
buf = stream.get_string(msg.bytesize)
|
|
119
122
|
assert_equal msg, buf
|
|
120
123
|
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
stream.clear
|
|
125
|
+
# numbers may vary with different kernel versions
|
|
126
|
+
assert_in_range 24..32, machine.metrics[:buffers_allocated]
|
|
127
|
+
assert_in_range 10..18, machine.metrics[:buffers_free]
|
|
123
128
|
assert_equal 256, machine.metrics[:segments_free]
|
|
124
|
-
assert_equal 65536 * 4, machine.metrics[:buffer_space_allocated]
|
|
125
|
-
|
|
126
129
|
ensure
|
|
127
130
|
machine.terminate(f)
|
|
128
131
|
machine.join(f)
|
|
@@ -136,7 +139,7 @@ class StreamTest < StreamBaseTest
|
|
|
136
139
|
|
|
137
140
|
assert_equal 'foo', stream.get_line(0)
|
|
138
141
|
|
|
139
|
-
assert_equal [
|
|
142
|
+
assert_equal [16, 0, 255, 16384 * 16, 16384 * 16 - 12], buffer_metrics
|
|
140
143
|
assert_equal 'bar', stream.get_line(0)
|
|
141
144
|
assert_nil stream.get_line(0)
|
|
142
145
|
assert_equal "baz", stream.get_string(-6)
|
|
@@ -152,11 +155,11 @@ class StreamTest < StreamBaseTest
|
|
|
152
155
|
machine.close(@wfd)
|
|
153
156
|
|
|
154
157
|
# three segments received
|
|
155
|
-
assert_equal [
|
|
158
|
+
assert_equal [16, 0, 253, 16384 * 16, 16384 * 16 - 13], buffer_metrics
|
|
156
159
|
assert_equal 'bar', stream.get_line(0)
|
|
157
|
-
assert_equal [
|
|
160
|
+
assert_equal [16, 0, 255, 16384 * 16, 16384 * 16 - 13], buffer_metrics
|
|
158
161
|
assert_equal 'baz', stream.get_line(0)
|
|
159
|
-
assert_equal [
|
|
162
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 13], buffer_metrics
|
|
160
163
|
assert_nil stream.get_line(0)
|
|
161
164
|
end
|
|
162
165
|
|
|
@@ -182,7 +185,7 @@ class StreamTest < StreamBaseTest
|
|
|
182
185
|
assert_equal 'bizz', stream.get_line(5)
|
|
183
186
|
|
|
184
187
|
assert_nil stream.get_line(8)
|
|
185
|
-
assert_equal [
|
|
188
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 17], buffer_metrics
|
|
186
189
|
end
|
|
187
190
|
|
|
188
191
|
def test_stream_get_string
|
|
@@ -467,6 +470,23 @@ class StreamDevRandomTest < UMBaseTest
|
|
|
467
470
|
machine.close(fd) rescue nil
|
|
468
471
|
end
|
|
469
472
|
|
|
473
|
+
def get_line_do(n, acc)
|
|
474
|
+
fd = @machine.open('/dev/random', UM::O_RDONLY)
|
|
475
|
+
stream = UM::Stream.new(@machine, fd)
|
|
476
|
+
n.times { acc << stream.get_line(0) }
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def test_stream_dev_random_get_line_concurrent
|
|
480
|
+
acc = []
|
|
481
|
+
c = 1
|
|
482
|
+
n = 100000
|
|
483
|
+
ff = c.times.map {
|
|
484
|
+
machine.spin { get_line_do(n, acc) }
|
|
485
|
+
}
|
|
486
|
+
machine.await(ff)
|
|
487
|
+
assert_equal c * n, acc.size
|
|
488
|
+
end
|
|
489
|
+
|
|
470
490
|
def test_stream_dev_random_get_string
|
|
471
491
|
fd = machine.open('/dev/random', UM::O_RDONLY)
|
|
472
492
|
stream = UM::Stream.new(machine, fd)
|
data/test/test_um.rb
CHANGED
|
@@ -292,6 +292,20 @@ class ScheduleTest < UMBaseTest
|
|
|
292
292
|
assert_kind_of TOError, e
|
|
293
293
|
end
|
|
294
294
|
|
|
295
|
+
def test_timeout_with_exception_instance
|
|
296
|
+
res = nil
|
|
297
|
+
e = TOError.new
|
|
298
|
+
begin
|
|
299
|
+
res = machine.timeout(0.01, e) {
|
|
300
|
+
machine.sleep(1)
|
|
301
|
+
:foo
|
|
302
|
+
}
|
|
303
|
+
rescue => res
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
assert_equal e, res
|
|
307
|
+
end
|
|
308
|
+
|
|
295
309
|
def test_timeout_with_raising_block
|
|
296
310
|
e = nil
|
|
297
311
|
begin
|
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: 0.29.
|
|
4
|
+
version: 0.29.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
@@ -67,6 +67,7 @@ files:
|
|
|
67
67
|
- benchmark/common.rb
|
|
68
68
|
- benchmark/dns_client.rb
|
|
69
69
|
- benchmark/gets.rb
|
|
70
|
+
- benchmark/gets_concurrent.rb
|
|
70
71
|
- benchmark/http_parse.rb
|
|
71
72
|
- benchmark/http_server_accept_queue.rb
|
|
72
73
|
- benchmark/http_server_multi_accept.rb
|