uringmachine 0.30.0 → 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 +12 -4
- data/README.md +46 -38
- data/TODO.md +56 -2
- data/benchmark/gets.rb +7 -7
- data/benchmark/gets_concurrent.rb +10 -10
- data/benchmark/http_parse.rb +14 -14
- 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 +20 -3
- data/ext/um/um.h +24 -18
- data/ext/um/um_connection.c +775 -0
- data/ext/um/um_connection_class.c +394 -0
- data/ext/um/um_ssl.c +37 -2
- data/ext/um/um_utils.c +1 -1
- data/grant-2025/final-report.md +2 -0
- data/grant-2025/journal.md +1 -1
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +16 -16
- data/test/{test_stream.rb → test_connection.rb} +290 -153
- data/test/test_um.rb +18 -18
- metadata +10 -6
- data/ext/um/um_stream.c +0 -706
- data/ext/um/um_stream_class.c +0 -317
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#include "um.h"
|
|
2
|
+
|
|
3
|
+
VALUE cConnection;
|
|
4
|
+
VALUE eConnectionRESPError;
|
|
5
|
+
|
|
6
|
+
VALUE SYM_fd;
|
|
7
|
+
VALUE SYM_socket;
|
|
8
|
+
VALUE SYM_ssl;
|
|
9
|
+
|
|
10
|
+
inline int connection_has_target_obj_p(struct um_connection *conn) {
|
|
11
|
+
switch (conn->mode) {
|
|
12
|
+
case CONNECTION_SSL:
|
|
13
|
+
case CONNECTION_STRING:
|
|
14
|
+
case CONNECTION_IO_BUFFER:
|
|
15
|
+
return true;
|
|
16
|
+
default:
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
inline void connection_mark_segments(struct um_connection *conn) {
|
|
22
|
+
struct um_segment *curr = conn->head;
|
|
23
|
+
while (curr) {
|
|
24
|
+
// rb_gc_mark_movable(curr->obj);
|
|
25
|
+
curr = curr->next;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
inline void connection_compact_segments(struct um_connection *conn) {
|
|
30
|
+
struct um_segment *curr = conn->head;
|
|
31
|
+
while (curr) {
|
|
32
|
+
// curr->obj = rb_gc_location(curr->obj);
|
|
33
|
+
curr = curr->next;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static void Connection_mark(void *ptr) {
|
|
38
|
+
struct um_connection *conn = ptr;
|
|
39
|
+
rb_gc_mark_movable(conn->self);
|
|
40
|
+
rb_gc_mark_movable(conn->machine->self);
|
|
41
|
+
|
|
42
|
+
if (connection_has_target_obj_p(conn)) {
|
|
43
|
+
rb_gc_mark_movable(conn->target);
|
|
44
|
+
connection_mark_segments(conn);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static void Connection_compact(void *ptr) {
|
|
49
|
+
struct um_connection *conn = ptr;
|
|
50
|
+
conn->self = rb_gc_location(conn->self);
|
|
51
|
+
|
|
52
|
+
if (connection_has_target_obj_p(conn)) {
|
|
53
|
+
conn->target = rb_gc_location(conn->target);
|
|
54
|
+
connection_compact_segments(conn);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static void Connection_free(void *ptr) {
|
|
59
|
+
struct um_connection *conn = ptr;
|
|
60
|
+
connection_clear(conn);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static const rb_data_type_t Connection_type = {
|
|
64
|
+
.wrap_struct_name = "UringMachine::Connection",
|
|
65
|
+
.function = {
|
|
66
|
+
.dmark = Connection_mark,
|
|
67
|
+
.dfree = Connection_free,
|
|
68
|
+
.dsize = NULL,
|
|
69
|
+
.dcompact = Connection_compact
|
|
70
|
+
},
|
|
71
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
static VALUE Connection_allocate(VALUE klass) {
|
|
75
|
+
struct um_connection *conn;
|
|
76
|
+
VALUE self = TypedData_Make_Struct(klass, struct um_connection, &Connection_type, conn);
|
|
77
|
+
return self;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static inline struct um_connection *um_get_connection(VALUE self) {
|
|
81
|
+
struct um_connection *conn;
|
|
82
|
+
TypedData_Get_Struct(self, struct um_connection, &Connection_type, conn);
|
|
83
|
+
return conn;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static inline void connection_set_target(struct um_connection *conn, VALUE target, enum um_connection_mode mode) {
|
|
87
|
+
conn->mode = mode;
|
|
88
|
+
switch (mode) {
|
|
89
|
+
case CONNECTION_FD:
|
|
90
|
+
case CONNECTION_SOCKET:
|
|
91
|
+
conn->fd = NUM2INT(target);
|
|
92
|
+
return;
|
|
93
|
+
case CONNECTION_SSL:
|
|
94
|
+
conn->target = target;
|
|
95
|
+
um_ssl_set_bio(conn->machine, target);
|
|
96
|
+
return;
|
|
97
|
+
default:
|
|
98
|
+
rb_raise(eUMError, "Invalid connection mode");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static inline void connection_setup(struct um_connection *conn, VALUE target, VALUE mode) {
|
|
103
|
+
conn->working_buffer = NULL;
|
|
104
|
+
if (NIL_P(mode)) {
|
|
105
|
+
if (TYPE(target) == T_DATA)
|
|
106
|
+
connection_set_target(conn, target, CONNECTION_SSL);
|
|
107
|
+
else
|
|
108
|
+
connection_set_target(conn, target, CONNECTION_FD);
|
|
109
|
+
}
|
|
110
|
+
else if (mode == SYM_fd)
|
|
111
|
+
connection_set_target(conn, target, CONNECTION_FD);
|
|
112
|
+
else if (mode == SYM_socket)
|
|
113
|
+
connection_set_target(conn, target, CONNECTION_SOCKET);
|
|
114
|
+
else if (mode == SYM_ssl)
|
|
115
|
+
connection_set_target(conn, target, CONNECTION_SSL);
|
|
116
|
+
else
|
|
117
|
+
rb_raise(eUMError, "Invalid connection mode");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* call-seq:
|
|
121
|
+
* UM::Stream.new(machine, fd, mode = nil) -> conn
|
|
122
|
+
* machine.connection(fd, mode = nil) -> conn
|
|
123
|
+
* machine.connection(fd, mode = nil) { |conn| ... }
|
|
124
|
+
*
|
|
125
|
+
* Initializes a new connection with the given UringMachine instance, target and
|
|
126
|
+
* optional mode. The target maybe a file descriptor, or an instance of
|
|
127
|
+
* OpenSSL::SSL::SSLSocket. In case of an SSL socket, the mode should be :ssl.
|
|
128
|
+
*
|
|
129
|
+
* @param machine [UringMachine] UringMachine instance
|
|
130
|
+
* @param target [integer, OpenSSL::SSL::SSLSocket] connection target: file descriptor or SSL socket
|
|
131
|
+
* @param mode [Symbol] optional connection mode: :fd, :socket, :ssl
|
|
132
|
+
* @return [void]
|
|
133
|
+
*/
|
|
134
|
+
VALUE Connection_initialize(int argc, VALUE *argv, VALUE self) {
|
|
135
|
+
VALUE machine;
|
|
136
|
+
VALUE target;
|
|
137
|
+
VALUE mode;
|
|
138
|
+
rb_scan_args(argc, argv, "21", &machine, &target, &mode);
|
|
139
|
+
|
|
140
|
+
struct um_connection *conn = um_get_connection(self);
|
|
141
|
+
memset(conn, 0, sizeof(struct um_connection));
|
|
142
|
+
|
|
143
|
+
RB_OBJ_WRITE(self, &conn->self, self);
|
|
144
|
+
conn->machine = um_get_machine(machine);
|
|
145
|
+
connection_setup(conn, target, mode);
|
|
146
|
+
|
|
147
|
+
return self;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* call-seq:
|
|
151
|
+
* conn.mode -> mode
|
|
152
|
+
*
|
|
153
|
+
* Returns the connection mode.
|
|
154
|
+
*
|
|
155
|
+
* @return [Symbol] connection mode
|
|
156
|
+
*/
|
|
157
|
+
VALUE Connection_mode(VALUE self) {
|
|
158
|
+
struct um_connection *conn = um_get_connection(self);
|
|
159
|
+
switch (conn->mode) {
|
|
160
|
+
case CONNECTION_FD: return SYM_fd;
|
|
161
|
+
case CONNECTION_SOCKET: return SYM_socket;
|
|
162
|
+
case CONNECTION_SSL: return SYM_ssl;
|
|
163
|
+
default: return Qnil;
|
|
164
|
+
}
|
|
165
|
+
return Qnil;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* call-seq:
|
|
169
|
+
* conn.read_line(limit) -> str
|
|
170
|
+
*
|
|
171
|
+
* Reads from the string until a newline character is encountered. Returns the
|
|
172
|
+
* line without the newline delimiter. If limit is 0, the line length is not
|
|
173
|
+
* limited. If no newline delimiter is found before EOF, returns nil.
|
|
174
|
+
*
|
|
175
|
+
* @param limit [integer] maximum line length (0 means no limit)
|
|
176
|
+
* @return [String, nil] read data or nil
|
|
177
|
+
*/
|
|
178
|
+
VALUE Connection_read_line(VALUE self, VALUE limit) {
|
|
179
|
+
struct um_connection *conn = um_get_connection(self);
|
|
180
|
+
return connection_read_line(conn, Qnil, NUM2ULONG(limit));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* call-seq:
|
|
184
|
+
* conn.read(len) -> str
|
|
185
|
+
*
|
|
186
|
+
* Reads len bytes from the conn. If len is 0, reads all available bytes. If
|
|
187
|
+
* len is negative, reads up to -len available bytes. If len is positive and eof
|
|
188
|
+
* is encountered before len bytes are read, returns nil.
|
|
189
|
+
*
|
|
190
|
+
* @param len [integer] number of bytes to read
|
|
191
|
+
* @return [String, nil] read data or nil
|
|
192
|
+
*/
|
|
193
|
+
VALUE Connection_read(VALUE self, VALUE len) {
|
|
194
|
+
struct um_connection *conn = um_get_connection(self);
|
|
195
|
+
return connection_read(conn, Qnil, NUM2LONG(len), 0, false);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* call-seq:
|
|
199
|
+
* conn.read_to_delim(delim, limit) -> str
|
|
200
|
+
*
|
|
201
|
+
* Reads from the string until a the given delimiter is encountered. Returns the
|
|
202
|
+
* line without the delimiter. If limit is 0, the length is not limited. If a
|
|
203
|
+
* delimiter is not found before EOF and limit is 0 or greater, returns nil.
|
|
204
|
+
*
|
|
205
|
+
* If no delimiter is found before EOF and limit is negative, returns the
|
|
206
|
+
* buffered data up to EOF or until the absolute-value length limit is reached.
|
|
207
|
+
*
|
|
208
|
+
* The `delim` parameter must be a single byte string.
|
|
209
|
+
*
|
|
210
|
+
* @param delim [String] delimiter (single byte) @param limit [integer] maximum
|
|
211
|
+
* line length (0 means no limit) @return [String, nil] read data or nil
|
|
212
|
+
*/
|
|
213
|
+
VALUE Connection_read_to_delim(VALUE self, VALUE delim, VALUE limit) {
|
|
214
|
+
struct um_connection *conn = um_get_connection(self);
|
|
215
|
+
return connection_read_to_delim(conn, Qnil, delim, NUM2LONG(limit));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* call-seq:
|
|
219
|
+
* conn.skip(len) -> len
|
|
220
|
+
*
|
|
221
|
+
* Skips len bytes in the conn.
|
|
222
|
+
*
|
|
223
|
+
* @param len [integer] number of bytes to skip
|
|
224
|
+
* @return [Integer] len
|
|
225
|
+
*/
|
|
226
|
+
VALUE Connection_skip(VALUE self, VALUE len) {
|
|
227
|
+
struct um_connection *conn = um_get_connection(self);
|
|
228
|
+
connection_skip(conn, NUM2LONG(len), true);
|
|
229
|
+
return len;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* call-seq:
|
|
233
|
+
* conn.read_each { |data| } -> conn
|
|
234
|
+
*
|
|
235
|
+
* Reads from the target, passing each chunk to the given block.
|
|
236
|
+
*
|
|
237
|
+
* @return [UringMachine::Connection] conn
|
|
238
|
+
*/
|
|
239
|
+
VALUE Connection_read_each(VALUE self) {
|
|
240
|
+
struct um_connection *conn = um_get_connection(self);
|
|
241
|
+
connection_read_each(conn);
|
|
242
|
+
return self;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* call-seq:
|
|
246
|
+
* conn.write(*bufs) -> len
|
|
247
|
+
*
|
|
248
|
+
* Writes to the connection, ensuring that all data has been written before
|
|
249
|
+
* returning the total number of bytes written.
|
|
250
|
+
*
|
|
251
|
+
* @param bufs [Array<String, IO::Buffer>] data to write
|
|
252
|
+
* @return [Integer] total bytes written
|
|
253
|
+
*/
|
|
254
|
+
VALUE Connection_write(int argc, VALUE *argv, VALUE self) {
|
|
255
|
+
struct um_connection *conn = um_get_connection(self);
|
|
256
|
+
return connection_writev(conn, argc, argv);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* call-seq:
|
|
260
|
+
* conn.resp_read -> obj
|
|
261
|
+
*
|
|
262
|
+
* Decodes an object from a RESP (Redis protocol) message.
|
|
263
|
+
*
|
|
264
|
+
* @return [any] decoded object
|
|
265
|
+
*/
|
|
266
|
+
VALUE Connection_resp_read(VALUE self) {
|
|
267
|
+
struct um_connection *conn = um_get_connection(self);
|
|
268
|
+
VALUE out_buffer = rb_utf8_str_new_literal("");
|
|
269
|
+
VALUE obj = resp_read(conn, out_buffer);
|
|
270
|
+
RB_GC_GUARD(out_buffer);
|
|
271
|
+
return obj;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* call-seq:
|
|
275
|
+
* conn.resp_write(obj) -> conn
|
|
276
|
+
*
|
|
277
|
+
* Writes the given object using RESP (Redis protocol) to the connection target.
|
|
278
|
+
* Returns the number of bytes written.
|
|
279
|
+
*
|
|
280
|
+
* @param obj [any] object to write
|
|
281
|
+
* @return [Integer] total bytes written
|
|
282
|
+
*/
|
|
283
|
+
VALUE Connection_resp_write(VALUE self, VALUE obj) {
|
|
284
|
+
struct um_connection *conn = um_get_connection(self);
|
|
285
|
+
|
|
286
|
+
VALUE str = rb_str_new(NULL, 0);
|
|
287
|
+
struct um_write_buffer buf;
|
|
288
|
+
write_buffer_init(&buf, str);
|
|
289
|
+
rb_str_modify(str);
|
|
290
|
+
resp_encode(&buf, obj);
|
|
291
|
+
write_buffer_update_len(&buf);
|
|
292
|
+
|
|
293
|
+
size_t len = connection_write_raw(conn, buf.ptr, buf.len);
|
|
294
|
+
RB_GC_GUARD(str);
|
|
295
|
+
return ULONG2NUM(len);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* call-seq:
|
|
299
|
+
* conn.resp_encode(obj) -> string
|
|
300
|
+
*
|
|
301
|
+
* Encodes an object into a RESP (Redis protocol) message.
|
|
302
|
+
*
|
|
303
|
+
* @param str [String] string buffer
|
|
304
|
+
* @param obj [any] object to be encoded
|
|
305
|
+
* @return [String] str
|
|
306
|
+
*/
|
|
307
|
+
VALUE Connection_resp_encode(VALUE self, VALUE str, VALUE obj) {
|
|
308
|
+
struct um_write_buffer buf;
|
|
309
|
+
write_buffer_init(&buf, str);
|
|
310
|
+
rb_str_modify(str);
|
|
311
|
+
resp_encode(&buf, obj);
|
|
312
|
+
write_buffer_update_len(&buf);
|
|
313
|
+
return str;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* call-seq:
|
|
317
|
+
* conn.eof? -> bool
|
|
318
|
+
*
|
|
319
|
+
* Returns true if connection has reached EOF.
|
|
320
|
+
*
|
|
321
|
+
* @return [bool] EOF reached
|
|
322
|
+
*/
|
|
323
|
+
VALUE Connection_eof_p(VALUE self) {
|
|
324
|
+
struct um_connection *conn = um_get_connection(self);
|
|
325
|
+
return conn->eof ? Qtrue : Qfalse;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* call-seq:
|
|
329
|
+
* conn.consumed -> int
|
|
330
|
+
*
|
|
331
|
+
* Returns the total number of bytes consumed from the conn.
|
|
332
|
+
*
|
|
333
|
+
* @return [Integer] total bytes consumed
|
|
334
|
+
*/
|
|
335
|
+
VALUE Connection_consumed(VALUE self) {
|
|
336
|
+
struct um_connection *conn = um_get_connection(self);
|
|
337
|
+
return LONG2NUM(conn->consumed_bytes);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* call-seq:
|
|
341
|
+
* conn.pending -> int
|
|
342
|
+
*
|
|
343
|
+
* Returns the number of bytes available for reading.
|
|
344
|
+
*
|
|
345
|
+
* @return [Integer] bytes available
|
|
346
|
+
*/
|
|
347
|
+
VALUE Connection_pending(VALUE self) {
|
|
348
|
+
struct um_connection *conn = um_get_connection(self);
|
|
349
|
+
return LONG2NUM(conn->pending_bytes);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* call-seq:
|
|
353
|
+
* conn.clear -> conn
|
|
354
|
+
*
|
|
355
|
+
* Clears all available bytes and stops any ongoing read operation.
|
|
356
|
+
*
|
|
357
|
+
* @return [UM::Stream] self
|
|
358
|
+
*/
|
|
359
|
+
VALUE Connection_clear(VALUE self) {
|
|
360
|
+
struct um_connection *conn = um_get_connection(self);
|
|
361
|
+
connection_clear(conn);
|
|
362
|
+
return self;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
void Init_Stream(void) {
|
|
366
|
+
cConnection = rb_define_class_under(cUM, "Connection", rb_cObject);
|
|
367
|
+
rb_define_alloc_func(cConnection, Connection_allocate);
|
|
368
|
+
|
|
369
|
+
rb_define_method(cConnection, "initialize", Connection_initialize, -1);
|
|
370
|
+
rb_define_method(cConnection, "mode", Connection_mode, 0);
|
|
371
|
+
|
|
372
|
+
rb_define_method(cConnection, "read_line", Connection_read_line, 1);
|
|
373
|
+
rb_define_method(cConnection, "read", Connection_read, 1);
|
|
374
|
+
rb_define_method(cConnection, "read_to_delim", Connection_read_to_delim, 2);
|
|
375
|
+
rb_define_method(cConnection, "skip", Connection_skip, 1);
|
|
376
|
+
rb_define_method(cConnection, "read_each", Connection_read_each, 0);
|
|
377
|
+
|
|
378
|
+
rb_define_method(cConnection, "write", Connection_write, -1);
|
|
379
|
+
|
|
380
|
+
rb_define_method(cConnection, "resp_read", Connection_resp_read, 0);
|
|
381
|
+
rb_define_method(cConnection, "resp_write", Connection_resp_write, 1);
|
|
382
|
+
rb_define_singleton_method(cConnection, "resp_encode", Connection_resp_encode, 2);
|
|
383
|
+
|
|
384
|
+
rb_define_method(cConnection, "eof?", Connection_eof_p, 0);
|
|
385
|
+
rb_define_method(cConnection, "consumed", Connection_consumed, 0);
|
|
386
|
+
rb_define_method(cConnection, "pending", Connection_pending, 0);
|
|
387
|
+
rb_define_method(cConnection, "clear", Connection_clear, 0);
|
|
388
|
+
|
|
389
|
+
eConnectionRESPError = rb_define_class_under(cConnection, "RESPError", rb_eStandardError);
|
|
390
|
+
|
|
391
|
+
SYM_fd = ID2SYM(rb_intern("fd"));
|
|
392
|
+
SYM_socket = ID2SYM(rb_intern("socket"));
|
|
393
|
+
SYM_ssl = ID2SYM(rb_intern("ssl"));
|
|
394
|
+
}
|
data/ext/um/um_ssl.c
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
static int um_bio_read(BIO *bio, char *buf, int blen)
|
|
9
9
|
{
|
|
10
10
|
struct um *machine = (struct um *)BIO_get_ex_data(bio, IDX_BIO_DATA_MACHINE);
|
|
11
|
-
|
|
11
|
+
int fd = (int)(long)BIO_get_ex_data(bio, IDX_BIO_DATA_FD);
|
|
12
12
|
return (int)um_read_raw(machine, fd, buf, blen);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
static int um_bio_write(BIO *bio, const char *buf, int blen)
|
|
16
16
|
{
|
|
17
17
|
struct um *machine = (struct um *)BIO_get_ex_data(bio, IDX_BIO_DATA_MACHINE);
|
|
18
|
-
|
|
18
|
+
int fd = (int)(long)BIO_get_ex_data(bio, IDX_BIO_DATA_FD);
|
|
19
19
|
return (int)um_write_raw(machine, fd, buf, blen);
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -105,3 +105,38 @@ int um_ssl_write(struct um *machine, VALUE ssl_obj, VALUE buf, size_t len) {
|
|
|
105
105
|
|
|
106
106
|
return ret;
|
|
107
107
|
}
|
|
108
|
+
|
|
109
|
+
int um_ssl_write_raw(struct um *machine, VALUE ssl_obj, const char *buffer, size_t len) {
|
|
110
|
+
SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
|
|
111
|
+
if (unlikely(!len)) return INT2NUM(0);
|
|
112
|
+
|
|
113
|
+
int ret = SSL_write(ssl, buffer, (int)len);
|
|
114
|
+
if (ret <= 0) rb_raise(eUMError, "Failed to write");
|
|
115
|
+
|
|
116
|
+
return ret;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
int um_ssl_write_all(struct um *machine, VALUE ssl_obj, VALUE buf) {
|
|
120
|
+
SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
|
|
121
|
+
const char *base;
|
|
122
|
+
size_t size;
|
|
123
|
+
um_get_buffer_bytes_for_writing(buf, (const void **)&base, &size, true);
|
|
124
|
+
|
|
125
|
+
size_t left = size;
|
|
126
|
+
while (left) {
|
|
127
|
+
int ret = SSL_write(ssl, base, (int)left);
|
|
128
|
+
if (ret <= 0) rb_raise(eUMError, "Failed to write");
|
|
129
|
+
|
|
130
|
+
left -= ret;
|
|
131
|
+
base += ret;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return size;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
int um_ssl_writev(struct um *machine, VALUE ssl, int argc, VALUE *argv) {
|
|
138
|
+
size_t total = 0;
|
|
139
|
+
for (int i = 0; i < argc; i++)
|
|
140
|
+
total += um_ssl_write_all(machine, ssl, argv[i]);
|
|
141
|
+
return total;
|
|
142
|
+
}
|
data/ext/um/um_utils.c
CHANGED
|
@@ -159,7 +159,7 @@ int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count) {
|
|
|
159
159
|
return bg_id;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
inline VALUE
|
|
162
|
+
inline VALUE um_read_from_buffer_ring(struct um *machine, int bgid, __s32 result, __u32 flags) {
|
|
163
163
|
if (!result) return Qnil;
|
|
164
164
|
|
|
165
165
|
unsigned buf_idx = flags >> IORING_CQE_BUFFER_SHIFT;
|
data/grant-2025/final-report.md
CHANGED
|
@@ -244,6 +244,8 @@ scheduler.join
|
|
|
244
244
|
- Added more low-level methods for performing I/O operations supported by
|
|
245
245
|
io_uring: `splice`, `tee`, `fsync`.
|
|
246
246
|
|
|
247
|
+
- Added RDoc [documentation](https://www.rubydoc.info/gems/uringmachine).
|
|
248
|
+
|
|
247
249
|
### Benchmarking
|
|
248
250
|
|
|
249
251
|
- I did extensive benchmarking comparing different solutions for performing
|
data/grant-2025/journal.md
CHANGED
|
@@ -121,7 +121,7 @@ Ruby I/O layer. Some interesting warts in the Ruby `IO` implementation:
|
|
|
121
121
|
```ruby
|
|
122
122
|
def io_write(io, buffer, length, offset)
|
|
123
123
|
reset_nonblock(io)
|
|
124
|
-
@machine.write(io.fileno, buffer.
|
|
124
|
+
@machine.write(io.fileno, buffer.read)
|
|
125
125
|
rescue Errno::EINTR
|
|
126
126
|
retry
|
|
127
127
|
end
|
data/lib/uringmachine/version.rb
CHANGED
data/lib/uringmachine.rb
CHANGED
|
@@ -195,28 +195,28 @@ class UringMachine
|
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
# call-seq:
|
|
198
|
-
# machine.
|
|
199
|
-
# machine.
|
|
198
|
+
# machine.connection(fd, mode = nil) -> conn
|
|
199
|
+
# machine.connection(fd, mode = nil) { |conn| }
|
|
200
200
|
#
|
|
201
|
-
# Creates a
|
|
202
|
-
# mode indicates the type of target and how it is read from:
|
|
201
|
+
# Creates a connection for reading from the given target fd or other object.
|
|
202
|
+
# The mode indicates the type of target and how it is read from:
|
|
203
203
|
#
|
|
204
|
-
# - :
|
|
205
|
-
# - :
|
|
204
|
+
# - :fd - read from the given fd using the buffer pool (default mode)
|
|
205
|
+
# - :socket - receive from the given socket fd using the buffer pool
|
|
206
206
|
# - :ssl - read from the given SSL connection
|
|
207
207
|
#
|
|
208
|
-
# If a block is given, the block will be called with the
|
|
209
|
-
# method will return the block's return value.
|
|
208
|
+
# If a block is given, the block will be called with the connection object and
|
|
209
|
+
# the method will return the block's return value.
|
|
210
210
|
#
|
|
211
211
|
# @param target [Integer, OpenSSL::SSL::SSLSocket] fd or ssl connection
|
|
212
|
-
# @param mode [Symbol, nil]
|
|
213
|
-
# @return [UringMachine::Stream]
|
|
214
|
-
def
|
|
215
|
-
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
res = yield(
|
|
219
|
-
|
|
212
|
+
# @param mode [Symbol, nil] connection mode
|
|
213
|
+
# @return [UringMachine::Stream] connection object
|
|
214
|
+
def connection(target, mode = nil)
|
|
215
|
+
conn = UM::Connection.new(self, target, mode)
|
|
216
|
+
return conn if !block_given?
|
|
217
|
+
|
|
218
|
+
res = yield(conn)
|
|
219
|
+
conn.clear
|
|
220
220
|
res
|
|
221
221
|
end
|
|
222
222
|
|