uringmachine 0.1 → 0.3
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 -0
- data/Rakefile +1 -1
- data/ext/um/um.c +228 -38
- data/ext/um/um.h +36 -8
- data/ext/um/um_class.c +98 -31
- data/ext/um/um_ext.c +0 -6
- data/ext/um/um_op.c +70 -6
- data/ext/um/um_utils.c +51 -1
- data/lib/uringmachine/version.rb +3 -1
- data/lib/uringmachine.rb +0 -3
- data/test/helper.rb +5 -12
- data/test/test_um.rb +259 -13
- data/uringmachine.gemspec +1 -1
- metadata +2 -6
- data/ext/um/iou.h +0 -101
- data/ext/um/op_ctx.c +0 -138
- data/ext/um/ring.c +0 -755
- data/test/test_iou.rb +0 -876
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7129b3c8605d5734f7152bddb4fa1b6f034fd1de26262eabfbfc2190846967bd
|
4
|
+
data.tar.gz: 902e6663bac65d56e45d67383e2cd3eb0601534ed2b451f4f76b2812ce6125b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 683db63642ddb5d98c9eb137f8121a8cb8cb0fe44456928fca78cf8e322cebc01078efa71379abcceae1a1889140bf5b39f6ac16348db0b77d9c8a5bb9cf5cd0
|
7
|
+
data.tar.gz: 850dce780030b6102371861805f831299d3ea113f8c02141515afe167b0e49f43a51812a6159a91b6335085b41fcb226713eeb963ae69d208818cb1f3cb303b3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 2024-10-04 Version 0.3
|
2
|
+
|
3
|
+
- Fix race condition affecting `#timeout` and `#sleep`.
|
4
|
+
- Add `#accept_each`
|
5
|
+
- Add `#accept`
|
6
|
+
|
7
|
+
# 2024-10-03 Version 0.2
|
8
|
+
|
9
|
+
- Remove old IOU code.
|
10
|
+
- Add `#read_each`
|
11
|
+
- Add `#read`
|
12
|
+
|
1
13
|
# 2024-10-03 Version 0.1
|
2
14
|
|
3
15
|
The basic fiber scheduling stuff
|
data/Rakefile
CHANGED
@@ -26,7 +26,7 @@ CLEAN.include "**/*.o", "**/*.so", "**/*.so.*", "**/*.a", "**/*.bundle", "**/*.j
|
|
26
26
|
|
27
27
|
task :release do
|
28
28
|
require_relative './lib/uringmachine/version'
|
29
|
-
version =
|
29
|
+
version = UringMachine::VERSION
|
30
30
|
|
31
31
|
puts 'Building uringmachine...'
|
32
32
|
`gem build uringmachine.gemspec`
|
data/ext/um/um.c
CHANGED
@@ -2,6 +2,54 @@
|
|
2
2
|
#include "ruby/thread.h"
|
3
3
|
#include <sys/mman.h>
|
4
4
|
|
5
|
+
void um_setup(struct um *machine) {
|
6
|
+
machine->ring_initialized = 0;
|
7
|
+
machine->unsubmitted_count = 0;
|
8
|
+
machine->buffer_ring_count = 0;
|
9
|
+
machine->pending_count = 0;
|
10
|
+
machine->runqueue_head = NULL;
|
11
|
+
machine->runqueue_tail = NULL;
|
12
|
+
machine->op_freelist = NULL;
|
13
|
+
machine->result_freelist = NULL;
|
14
|
+
|
15
|
+
unsigned prepared_limit = 4096;
|
16
|
+
int flags = 0;
|
17
|
+
#ifdef HAVE_IORING_SETUP_SUBMIT_ALL
|
18
|
+
flags |= IORING_SETUP_SUBMIT_ALL;
|
19
|
+
#endif
|
20
|
+
#ifdef HAVE_IORING_SETUP_COOP_TASKRUN
|
21
|
+
flags |= IORING_SETUP_COOP_TASKRUN;
|
22
|
+
#endif
|
23
|
+
|
24
|
+
while (1) {
|
25
|
+
int ret = io_uring_queue_init(prepared_limit, &machine->ring, flags);
|
26
|
+
if (likely(!ret)) break;
|
27
|
+
|
28
|
+
// if ENOMEM is returned, try with half as much entries
|
29
|
+
if (unlikely(ret == -ENOMEM && prepared_limit > 64))
|
30
|
+
prepared_limit = prepared_limit / 2;
|
31
|
+
else
|
32
|
+
rb_syserr_fail(-ret, strerror(-ret));
|
33
|
+
}
|
34
|
+
machine->ring_initialized = 1;
|
35
|
+
}
|
36
|
+
|
37
|
+
inline void um_teardown(struct um *machine) {
|
38
|
+
if (!machine->ring_initialized) return;
|
39
|
+
|
40
|
+
for (unsigned i = 0; i < machine->buffer_ring_count; i++) {
|
41
|
+
struct buf_ring_descriptor *desc = machine->buffer_rings + i;
|
42
|
+
io_uring_free_buf_ring(&machine->ring, desc->br, desc->buf_count, i);
|
43
|
+
free(desc->buf_base);
|
44
|
+
}
|
45
|
+
machine->buffer_ring_count = 0;
|
46
|
+
io_uring_queue_exit(&machine->ring);
|
47
|
+
machine->ring_initialized = 0;
|
48
|
+
|
49
|
+
um_free_op_linked_list(machine, machine->op_freelist);
|
50
|
+
um_free_op_linked_list(machine, machine->runqueue_head);
|
51
|
+
}
|
52
|
+
|
5
53
|
static inline struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op) {
|
6
54
|
struct io_uring_sqe *sqe;
|
7
55
|
sqe = io_uring_get_sqe(&machine->ring);
|
@@ -24,22 +72,6 @@ done:
|
|
24
72
|
return sqe;
|
25
73
|
}
|
26
74
|
|
27
|
-
inline void um_cleanup(struct um *machine) {
|
28
|
-
if (!machine->ring_initialized) return;
|
29
|
-
|
30
|
-
for (unsigned i = 0; i < machine->buffer_ring_count; i++) {
|
31
|
-
struct buf_ring_descriptor *desc = machine->buffer_rings + i;
|
32
|
-
io_uring_free_buf_ring(&machine->ring, desc->br, desc->buf_count, i);
|
33
|
-
free(desc->buf_base);
|
34
|
-
}
|
35
|
-
machine->buffer_ring_count = 0;
|
36
|
-
io_uring_queue_exit(&machine->ring);
|
37
|
-
machine->ring_initialized = 0;
|
38
|
-
|
39
|
-
um_free_linked_list(machine->runqueue_head);
|
40
|
-
um_free_linked_list(machine->freelist_head);
|
41
|
-
}
|
42
|
-
|
43
75
|
struct wait_for_cqe_ctx {
|
44
76
|
struct um *machine;
|
45
77
|
struct io_uring_cqe *cqe;
|
@@ -57,26 +89,47 @@ void *um_wait_for_cqe_without_gvl(void *ptr) {
|
|
57
89
|
return NULL;
|
58
90
|
}
|
59
91
|
|
92
|
+
inline void um_handle_submitted_op_cqe_single(struct um *machine, struct um_op *op, struct io_uring_cqe *cqe) {
|
93
|
+
op->cqe_result = cqe->res;
|
94
|
+
op->cqe_flags = cqe->flags;
|
95
|
+
op->state = OP_completed;
|
96
|
+
um_runqueue_push(machine, op);
|
97
|
+
}
|
98
|
+
|
99
|
+
inline void um_handle_submitted_op_cqe_multi(struct um *machine, struct um_op *op, struct io_uring_cqe *cqe) {
|
100
|
+
if (!op->results_head) {
|
101
|
+
struct um_op *op2 = um_op_checkout(machine);
|
102
|
+
op2->state = OP_schedule;
|
103
|
+
op2->fiber = op->fiber;
|
104
|
+
op2->resume_value = Qnil;
|
105
|
+
um_runqueue_push(machine, op2);
|
106
|
+
}
|
107
|
+
um_op_result_push(machine, op, cqe->res, cqe->flags);
|
108
|
+
|
109
|
+
if (!(cqe->flags & IORING_CQE_F_MORE))
|
110
|
+
op->state = OP_completed;
|
111
|
+
}
|
112
|
+
|
60
113
|
inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe) {
|
61
114
|
struct um_op *op = (struct um_op *)cqe->user_data;
|
62
|
-
if (!op) return;
|
115
|
+
if (unlikely(!op)) return;
|
63
116
|
|
64
117
|
switch (op->state) {
|
65
118
|
case OP_submitted:
|
66
|
-
if (cqe->res == -ECANCELED) {
|
119
|
+
if (unlikely(cqe->res == -ECANCELED)) {
|
67
120
|
um_op_checkin(machine, op);
|
121
|
+
break;
|
68
122
|
}
|
69
|
-
|
70
|
-
op
|
71
|
-
|
72
|
-
op
|
73
|
-
um_runqueue_push(machine, op);
|
74
|
-
}
|
123
|
+
if (!op->is_multishot)
|
124
|
+
um_handle_submitted_op_cqe_single(machine, op, cqe);
|
125
|
+
else
|
126
|
+
um_handle_submitted_op_cqe_multi(machine, op, cqe);
|
75
127
|
break;
|
76
128
|
case OP_abandonned:
|
77
129
|
// op has been abandonned by the I/O method, so we need to cleanup (check
|
78
130
|
// the op in to the free list).
|
79
131
|
um_op_checkin(machine, op);
|
132
|
+
break;
|
80
133
|
default:
|
81
134
|
// TODO: invalid state, should raise!
|
82
135
|
}
|
@@ -173,19 +226,26 @@ static inline void um_cancel_op(struct um *machine, struct um_op *op) {
|
|
173
226
|
io_uring_prep_cancel64(sqe, (long long)op, 0);
|
174
227
|
}
|
175
228
|
|
176
|
-
static inline VALUE um_await_op(struct um *machine, struct um_op *op) {
|
229
|
+
static inline VALUE um_await_op(struct um *machine, struct um_op *op, int *result, int *flags) {
|
177
230
|
op->fiber = rb_fiber_current();
|
178
231
|
VALUE v = um_fiber_switch(machine);
|
179
|
-
|
180
232
|
int is_exception = um_value_is_exception_p(v);
|
181
233
|
|
182
|
-
if (is_exception && op->state == OP_submitted) {
|
234
|
+
if (unlikely(is_exception && op->state == OP_submitted)) {
|
183
235
|
um_cancel_op(machine, op);
|
184
236
|
op->state = OP_abandonned;
|
185
237
|
}
|
186
|
-
else
|
187
|
-
|
188
|
-
|
238
|
+
else {
|
239
|
+
// We copy over the CQE result and flags, since the op is immediately
|
240
|
+
// checked in.
|
241
|
+
if (result) *result = op->cqe_result;
|
242
|
+
if (flags) *flags = op->cqe_flags;
|
243
|
+
if (!op->is_multishot)
|
244
|
+
um_op_checkin(machine, op);
|
245
|
+
}
|
246
|
+
|
247
|
+
if (unlikely(is_exception)) um_raise_exception(v);
|
248
|
+
return v;
|
189
249
|
}
|
190
250
|
|
191
251
|
inline VALUE um_await(struct um *machine) {
|
@@ -216,13 +276,14 @@ inline void um_interrupt(struct um *machine, VALUE fiber, VALUE value) {
|
|
216
276
|
}
|
217
277
|
}
|
218
278
|
|
219
|
-
struct
|
279
|
+
struct op_ensure_ctx {
|
220
280
|
struct um *machine;
|
221
281
|
struct um_op *op;
|
282
|
+
int bgid;
|
222
283
|
};
|
223
284
|
|
224
285
|
VALUE um_timeout_ensure(VALUE arg) {
|
225
|
-
struct
|
286
|
+
struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
|
226
287
|
|
227
288
|
if (ctx->op->state == OP_submitted) {
|
228
289
|
// A CQE has not yet been received, we cancel the timeout and abandon the op
|
@@ -242,26 +303,155 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
|
|
242
303
|
if (!ID_new) ID_new = rb_intern("new");
|
243
304
|
|
244
305
|
struct um_op *op = um_op_checkout(machine);
|
245
|
-
|
306
|
+
op->ts = um_double_to_timespec(NUM2DBL(interval));
|
246
307
|
|
247
308
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
248
|
-
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
309
|
+
io_uring_prep_timeout(sqe, &op->ts, 0, 0);
|
249
310
|
op->state = OP_submitted;
|
250
311
|
op->fiber = rb_fiber_current();
|
251
312
|
op->resume_value = rb_funcall(class, ID_new, 0);
|
252
313
|
|
253
|
-
struct
|
314
|
+
struct op_ensure_ctx ctx = { .machine = machine, .op = op };
|
254
315
|
return rb_ensure(rb_yield, Qnil, um_timeout_ensure, (VALUE)&ctx);
|
255
316
|
}
|
256
317
|
|
257
318
|
inline VALUE um_sleep(struct um *machine, double duration) {
|
258
319
|
struct um_op *op = um_op_checkout(machine);
|
259
|
-
|
320
|
+
op->ts = um_double_to_timespec(duration);
|
321
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
322
|
+
int result = 0;
|
323
|
+
|
324
|
+
io_uring_prep_timeout(sqe, &op->ts, 0, 0);
|
325
|
+
op->state = OP_submitted;
|
326
|
+
|
327
|
+
return um_await_op(machine, op, &result, NULL);
|
328
|
+
}
|
329
|
+
|
330
|
+
inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
|
331
|
+
struct um_op *op = um_op_checkout(machine);
|
332
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
333
|
+
int result = 0;
|
334
|
+
int flags = 0;
|
335
|
+
|
336
|
+
void *ptr = um_prepare_read_buffer(buffer, maxlen, buffer_offset);
|
337
|
+
io_uring_prep_read(sqe, fd, ptr, maxlen, -1);
|
338
|
+
op->state = OP_submitted;
|
260
339
|
|
340
|
+
um_await_op(machine, op, &result, &flags);
|
341
|
+
|
342
|
+
um_raise_on_system_error(result);
|
343
|
+
um_update_read_buffer(machine, buffer, buffer_offset, result, flags);
|
344
|
+
return INT2FIX(result);
|
345
|
+
}
|
346
|
+
|
347
|
+
VALUE um_multishot_ensure(VALUE arg) {
|
348
|
+
struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
|
349
|
+
switch (ctx->op->state) {
|
350
|
+
case OP_submitted:
|
351
|
+
um_cancel_op(ctx->machine, ctx->op);
|
352
|
+
break;
|
353
|
+
case OP_completed:
|
354
|
+
um_op_checkin(ctx->machine, ctx->op);
|
355
|
+
break;
|
356
|
+
default:
|
357
|
+
}
|
358
|
+
return Qnil;
|
359
|
+
}
|
360
|
+
|
361
|
+
VALUE um_read_each_safe_loop(VALUE arg) {
|
362
|
+
struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
|
363
|
+
int result = 0;
|
364
|
+
int flags = 0;
|
365
|
+
int total = 0;
|
366
|
+
|
367
|
+
while (1) {
|
368
|
+
um_await_op(ctx->machine, ctx->op, NULL, NULL);
|
369
|
+
if (!ctx->op->results_head) {
|
370
|
+
// TODO: raise, this shouldn't happen
|
371
|
+
printf("no result found!\n");
|
372
|
+
}
|
373
|
+
while (um_op_result_shift(ctx->machine, ctx->op, &result, &flags)) {
|
374
|
+
if (likely(result > 0)) {
|
375
|
+
total += result;
|
376
|
+
VALUE buf = get_string_from_buffer_ring(ctx->machine, ctx->bgid, result, flags);
|
377
|
+
rb_yield(buf);
|
378
|
+
}
|
379
|
+
else
|
380
|
+
return INT2FIX(total);
|
381
|
+
}
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
VALUE um_read_each(struct um *machine, int fd, int bgid) {
|
386
|
+
struct um_op *op = um_op_checkout(machine);
|
261
387
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
262
|
-
|
388
|
+
|
389
|
+
op->is_multishot = 1;
|
390
|
+
io_uring_prep_read_multishot(sqe, fd, 0, -1, bgid);
|
263
391
|
op->state = OP_submitted;
|
264
392
|
|
265
|
-
|
393
|
+
struct op_ensure_ctx ctx = { .machine = machine, .op = op, .bgid = bgid };
|
394
|
+
return rb_ensure(um_read_each_safe_loop, (VALUE)&ctx, um_multishot_ensure, (VALUE)&ctx);
|
266
395
|
}
|
267
396
|
|
397
|
+
VALUE um_write(struct um *machine, int fd, VALUE buffer, int len) {
|
398
|
+
struct um_op *op = um_op_checkout(machine);
|
399
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
400
|
+
int result = 0;
|
401
|
+
int flags = 0;
|
402
|
+
|
403
|
+
io_uring_prep_write(sqe, fd, RSTRING_PTR(buffer), len, -1);
|
404
|
+
op->state = OP_submitted;
|
405
|
+
|
406
|
+
um_await_op(machine, op, &result, &flags);
|
407
|
+
um_raise_on_system_error(result);
|
408
|
+
return INT2FIX(result);
|
409
|
+
}
|
410
|
+
|
411
|
+
VALUE um_accept(struct um *machine, int fd) {
|
412
|
+
struct um_op *op = um_op_checkout(machine);
|
413
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
414
|
+
struct sockaddr addr;
|
415
|
+
socklen_t len;
|
416
|
+
int result = 0;
|
417
|
+
int flags = 0;
|
418
|
+
io_uring_prep_accept(sqe, fd, &addr, &len, 0);
|
419
|
+
op->state = OP_submitted;
|
420
|
+
|
421
|
+
um_await_op(machine, op, &result, &flags);
|
422
|
+
um_raise_on_system_error(result);
|
423
|
+
return INT2FIX(result);
|
424
|
+
}
|
425
|
+
|
426
|
+
VALUE um_accept_each_safe_loop(VALUE arg) {
|
427
|
+
struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
|
428
|
+
int result = 0;
|
429
|
+
int flags = 0;
|
430
|
+
|
431
|
+
while (1) {
|
432
|
+
um_await_op(ctx->machine, ctx->op, NULL, NULL);
|
433
|
+
if (!ctx->op->results_head) {
|
434
|
+
// TODO: raise, this shouldn't happen
|
435
|
+
printf("no result found!\n");
|
436
|
+
}
|
437
|
+
while (um_op_result_shift(ctx->machine, ctx->op, &result, &flags)) {
|
438
|
+
if (likely(result > 0))
|
439
|
+
rb_yield(INT2FIX(result));
|
440
|
+
else
|
441
|
+
return Qnil;
|
442
|
+
}
|
443
|
+
}
|
444
|
+
}
|
445
|
+
|
446
|
+
VALUE um_accept_each(struct um *machine, int fd) {
|
447
|
+
struct um_op *op = um_op_checkout(machine);
|
448
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
449
|
+
struct sockaddr addr;
|
450
|
+
socklen_t len;
|
451
|
+
io_uring_prep_multishot_accept(sqe, fd, &addr, &len, 0);
|
452
|
+
op->state = OP_submitted;
|
453
|
+
op->is_multishot = 1;
|
454
|
+
|
455
|
+
struct op_ensure_ctx ctx = { .machine = machine, .op = op };
|
456
|
+
return rb_ensure(um_accept_each_safe_loop, (VALUE)&ctx, um_multishot_ensure, (VALUE)&ctx);
|
457
|
+
}
|
data/ext/um/um.h
CHANGED
@@ -29,13 +29,27 @@ enum op_state {
|
|
29
29
|
OP_schedule, // the corresponding fiber is scheduled
|
30
30
|
};
|
31
31
|
|
32
|
+
struct um_result_entry {
|
33
|
+
struct um_result_entry *next;
|
34
|
+
|
35
|
+
int result;
|
36
|
+
int flags;
|
37
|
+
};
|
38
|
+
|
32
39
|
struct um_op {
|
33
40
|
enum op_state state;
|
34
41
|
struct um_op *prev;
|
35
42
|
struct um_op *next;
|
43
|
+
|
44
|
+
// linked list for multishot results
|
45
|
+
struct um_result_entry *results_head;
|
46
|
+
struct um_result_entry *results_tail;
|
36
47
|
|
37
48
|
VALUE fiber;
|
38
49
|
VALUE resume_value;
|
50
|
+
int is_multishot;
|
51
|
+
struct __kernel_timespec ts;
|
52
|
+
|
39
53
|
int cqe_result;
|
40
54
|
int cqe_flags;
|
41
55
|
};
|
@@ -51,7 +65,9 @@ struct buf_ring_descriptor {
|
|
51
65
|
#define BUFFER_RING_MAX_COUNT 10
|
52
66
|
|
53
67
|
struct um {
|
54
|
-
struct um_op *
|
68
|
+
struct um_op *op_freelist;
|
69
|
+
struct um_result_entry *result_freelist;
|
70
|
+
|
55
71
|
struct um_op *runqueue_head;
|
56
72
|
struct um_op *runqueue_tail;
|
57
73
|
|
@@ -67,31 +83,43 @@ struct um {
|
|
67
83
|
|
68
84
|
extern VALUE cUM;
|
69
85
|
|
86
|
+
void um_setup(struct um *machine);
|
87
|
+
void um_teardown(struct um *machine);
|
88
|
+
void um_free_op_linked_list(struct um *machine, struct um_op *op);
|
89
|
+
void um_free_result_linked_list(struct um *machine, struct um_result_entry *entry);
|
90
|
+
|
70
91
|
struct __kernel_timespec um_double_to_timespec(double value);
|
92
|
+
int um_value_is_exception_p(VALUE v);
|
93
|
+
VALUE um_raise_exception(VALUE v);
|
94
|
+
void um_raise_on_system_error(int result);
|
71
95
|
|
72
|
-
void
|
96
|
+
void * um_prepare_read_buffer(VALUE buffer, unsigned len, int ofs);
|
97
|
+
void um_update_read_buffer(struct um *machine, VALUE buffer, int buffer_offset, int result, int flags);
|
98
|
+
VALUE get_string_from_buffer_ring(struct um *machine, int bgid, int result, int flags);
|
73
99
|
|
74
|
-
void um_free_linked_list(struct um_op *op);
|
75
100
|
VALUE um_fiber_switch(struct um *machine);
|
76
101
|
VALUE um_await(struct um *machine);
|
77
102
|
|
78
103
|
void um_op_checkin(struct um *machine, struct um_op *op);
|
79
104
|
struct um_op* um_op_checkout(struct um *machine);
|
80
|
-
|
81
|
-
|
105
|
+
void um_op_result_push(struct um *machine, struct um_op *op, int result, int flags);
|
106
|
+
int um_op_result_shift(struct um *machine, struct um_op *op, int *result, int *flags);
|
82
107
|
|
83
108
|
struct um_op *um_runqueue_find_by_fiber(struct um *machine, VALUE fiber);
|
84
109
|
void um_runqueue_push(struct um *machine, struct um_op *op);
|
85
110
|
struct um_op *um_runqueue_shift(struct um *machine);
|
86
111
|
void um_runqueue_unshift(struct um *machine, struct um_op *op);
|
87
112
|
|
88
|
-
int um_value_is_exception_p(VALUE v);
|
89
|
-
|
90
|
-
|
91
113
|
void um_schedule(struct um *machine, VALUE fiber, VALUE value);
|
92
114
|
void um_interrupt(struct um *machine, VALUE fiber, VALUE value);
|
93
115
|
VALUE um_timeout(struct um *machine, VALUE interval, VALUE class);
|
94
116
|
|
95
117
|
VALUE um_sleep(struct um *machine, double duration);
|
118
|
+
VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset);
|
119
|
+
VALUE um_read_each(struct um *machine, int fd, int bgid);
|
120
|
+
VALUE um_write(struct um *machine, int fd, VALUE buffer, int len);
|
121
|
+
|
122
|
+
VALUE um_accept(struct um *machine, int fd);
|
123
|
+
VALUE um_accept_each(struct um *machine, int fd);
|
96
124
|
|
97
125
|
#endif // UM_H
|
data/ext/um/um_class.c
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#include "um.h"
|
2
|
+
#include <sys/mman.h>
|
2
3
|
|
3
4
|
VALUE cUM;
|
4
5
|
|
@@ -13,7 +14,7 @@ static void UM_compact(void *ptr) {
|
|
13
14
|
}
|
14
15
|
|
15
16
|
static void UM_free(void *ptr) {
|
16
|
-
|
17
|
+
um_teardown((struct um *)ptr);
|
17
18
|
free(ptr);
|
18
19
|
}
|
19
20
|
|
@@ -42,37 +43,58 @@ inline struct um *get_machine(VALUE self) {
|
|
42
43
|
|
43
44
|
VALUE UM_initialize(VALUE self) {
|
44
45
|
struct um *machine = RTYPEDDATA_DATA(self);
|
46
|
+
um_setup(machine);
|
47
|
+
return self;
|
48
|
+
}
|
49
|
+
|
50
|
+
VALUE UM_setup_buffer_ring(VALUE self, VALUE size, VALUE count) {
|
51
|
+
struct um *machine = get_machine(self);
|
45
52
|
|
46
|
-
machine->
|
47
|
-
|
48
|
-
|
49
|
-
machine->
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
53
|
+
if (machine->buffer_ring_count == BUFFER_RING_MAX_COUNT)
|
54
|
+
rb_raise(rb_eRuntimeError, "Cannot setup more than BUFFER_RING_MAX_COUNT buffer rings");
|
55
|
+
|
56
|
+
struct buf_ring_descriptor *desc = machine->buffer_rings + machine->buffer_ring_count;
|
57
|
+
desc->buf_count = NUM2UINT(count);
|
58
|
+
desc->buf_size = NUM2UINT(size);
|
59
|
+
|
60
|
+
desc->br_size = sizeof(struct io_uring_buf) * desc->buf_count;
|
61
|
+
void *mapped = mmap(
|
62
|
+
NULL, desc->br_size, PROT_READ | PROT_WRITE,
|
63
|
+
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0
|
64
|
+
);
|
65
|
+
if (mapped == MAP_FAILED)
|
66
|
+
rb_raise(rb_eRuntimeError, "Failed to allocate buffer ring");
|
67
|
+
|
68
|
+
desc->br = (struct io_uring_buf_ring *)mapped;
|
69
|
+
io_uring_buf_ring_init(desc->br);
|
70
|
+
|
71
|
+
unsigned bg_id = machine->buffer_ring_count;
|
72
|
+
struct io_uring_buf_reg reg = {
|
73
|
+
.ring_addr = (unsigned long)desc->br,
|
74
|
+
.ring_entries = desc->buf_count,
|
75
|
+
.bgid = bg_id
|
76
|
+
};
|
77
|
+
int ret = io_uring_register_buf_ring(&machine->ring, ®, 0);
|
78
|
+
if (ret) {
|
79
|
+
munmap(desc->br, desc->br_size);
|
80
|
+
rb_syserr_fail(-ret, strerror(-ret));
|
81
|
+
}
|
82
|
+
|
83
|
+
desc->buf_base = malloc(desc->buf_count * desc->buf_size);
|
84
|
+
if (!desc->buf_base) {
|
85
|
+
io_uring_free_buf_ring(&machine->ring, desc->br, desc->buf_count, bg_id);
|
86
|
+
rb_raise(rb_eRuntimeError, "Failed to allocate buffers");
|
72
87
|
}
|
73
|
-
machine->ring_initialized = 1;
|
74
88
|
|
75
|
-
|
89
|
+
int mask = io_uring_buf_ring_mask(desc->buf_count);
|
90
|
+
for (unsigned i = 0; i < desc->buf_count; i++) {
|
91
|
+
io_uring_buf_ring_add(
|
92
|
+
desc->br, desc->buf_base + i * desc->buf_size, desc->buf_size,
|
93
|
+
i, mask, i);
|
94
|
+
}
|
95
|
+
io_uring_buf_ring_advance(desc->br, desc->buf_count);
|
96
|
+
machine->buffer_ring_count++;
|
97
|
+
return UINT2NUM(bg_id);
|
76
98
|
}
|
77
99
|
|
78
100
|
VALUE UM_pending_count(VALUE self) {
|
@@ -114,6 +136,46 @@ VALUE UM_sleep(VALUE self, VALUE duration) {
|
|
114
136
|
return duration;
|
115
137
|
}
|
116
138
|
|
139
|
+
VALUE UM_read(int argc, VALUE *argv, VALUE self) {
|
140
|
+
struct um *machine = get_machine(self);
|
141
|
+
VALUE fd;
|
142
|
+
VALUE buffer;
|
143
|
+
VALUE maxlen;
|
144
|
+
VALUE buffer_offset;
|
145
|
+
rb_scan_args(argc, argv, "31", &fd, &buffer, &maxlen, &buffer_offset);
|
146
|
+
|
147
|
+
return um_read(
|
148
|
+
machine, NUM2INT(fd), buffer, NUM2INT(maxlen),
|
149
|
+
NIL_P(buffer_offset) ? 0 : NUM2INT(buffer_offset)
|
150
|
+
);
|
151
|
+
}
|
152
|
+
|
153
|
+
VALUE UM_read_each(VALUE self, VALUE fd, VALUE bgid) {
|
154
|
+
struct um *machine = get_machine(self);
|
155
|
+
return um_read_each(machine, NUM2INT(fd), NUM2INT(bgid));
|
156
|
+
}
|
157
|
+
|
158
|
+
VALUE UM_write(int argc, VALUE *argv, VALUE self) {
|
159
|
+
struct um *machine = get_machine(self);
|
160
|
+
VALUE fd;
|
161
|
+
VALUE buffer;
|
162
|
+
VALUE len;
|
163
|
+
rb_scan_args(argc, argv, "21", &fd, &buffer, &len);
|
164
|
+
|
165
|
+
int bytes = NIL_P(len) ? RSTRING_LEN(buffer) : NUM2INT(len);
|
166
|
+
return um_write(machine, NUM2INT(fd), buffer, bytes);
|
167
|
+
}
|
168
|
+
|
169
|
+
VALUE UM_accept(VALUE self, VALUE fd) {
|
170
|
+
struct um *machine = get_machine(self);
|
171
|
+
return um_accept(machine, NUM2INT(fd));
|
172
|
+
}
|
173
|
+
|
174
|
+
VALUE UM_accept_each(VALUE self, VALUE fd) {
|
175
|
+
struct um *machine = get_machine(self);
|
176
|
+
return um_accept_each(machine, NUM2INT(fd));
|
177
|
+
}
|
178
|
+
|
117
179
|
void Init_UM(void) {
|
118
180
|
rb_ext_ractor_safe(true);
|
119
181
|
|
@@ -121,8 +183,7 @@ void Init_UM(void) {
|
|
121
183
|
rb_define_alloc_func(cUM, UM_allocate);
|
122
184
|
|
123
185
|
rb_define_method(cUM, "initialize", UM_initialize, 0);
|
124
|
-
|
125
|
-
|
186
|
+
rb_define_method(cUM, "setup_buffer_ring", UM_setup_buffer_ring, 2);
|
126
187
|
rb_define_method(cUM, "pending_count", UM_pending_count, 0);
|
127
188
|
|
128
189
|
rb_define_method(cUM, "snooze", UM_snooze, 0);
|
@@ -132,6 +193,12 @@ void Init_UM(void) {
|
|
132
193
|
rb_define_method(cUM, "timeout", UM_timeout, 2);
|
133
194
|
|
134
195
|
rb_define_method(cUM, "sleep", UM_sleep, 1);
|
196
|
+
rb_define_method(cUM, "read", UM_read, -1);
|
197
|
+
rb_define_method(cUM, "read_each", UM_read_each, 2);
|
198
|
+
rb_define_method(cUM, "write", UM_write, -1);
|
199
|
+
|
200
|
+
rb_define_method(cUM, "accept", UM_accept, 1);
|
201
|
+
rb_define_method(cUM, "accept_each", UM_accept_each, 1);
|
135
202
|
|
136
203
|
// rb_define_method(cUM, "emit", UM_emit, 1);
|
137
204
|
|