uringmachine 0.21.0 → 0.22.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/.rubocop.yml +2 -0
- data/CHANGELOG.md +14 -0
- data/TODO.md +144 -0
- data/benchmark/README.md +173 -0
- data/benchmark/bm_io_pipe.rb +70 -0
- data/benchmark/bm_io_socketpair.rb +71 -0
- data/benchmark/bm_mutex_cpu.rb +57 -0
- data/benchmark/bm_mutex_io.rb +64 -0
- data/benchmark/bm_pg_client.rb +109 -0
- data/benchmark/bm_queue.rb +76 -0
- data/benchmark/chart.png +0 -0
- data/benchmark/common.rb +135 -0
- data/benchmark/dns_client.rb +47 -0
- data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
- data/benchmark/run_bm.rb +8 -0
- data/benchmark/sqlite.rb +108 -0
- data/{examples/bm_write.rb → benchmark/write.rb} +4 -4
- data/ext/um/um.c +189 -100
- data/ext/um/um.h +36 -10
- data/ext/um/um_async_op.c +1 -1
- data/ext/um/um_class.c +87 -13
- data/ext/um/um_op.c +6 -0
- data/ext/um/um_sync.c +2 -2
- data/ext/um/um_utils.c +16 -0
- data/grant-2025/journal.md +118 -1
- data/grant-2025/tasks.md +48 -22
- data/lib/uringmachine/actor.rb +8 -0
- data/lib/uringmachine/dns_resolver.rb +1 -2
- data/lib/uringmachine/fiber_scheduler.rb +127 -81
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +32 -3
- data/test/helper.rb +7 -18
- data/test/test_actor.rb +12 -3
- data/test/test_async_op.rb +10 -10
- data/test/test_fiber.rb +84 -1
- data/test/test_fiber_scheduler.rb +950 -47
- data/test/test_um.rb +297 -120
- data/uringmachine.gemspec +2 -1
- metadata +38 -16
- data/examples/bm_fileno.rb +0 -33
- data/examples/bm_queue.rb +0 -111
- data/examples/bm_side_running.rb +0 -83
- data/examples/bm_sqlite.rb +0 -89
- data/examples/dns_client.rb +0 -12
- /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
- /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
- /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
- /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
data/ext/um/um.c
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#include <assert.h>
|
|
5
5
|
#include <poll.h>
|
|
6
6
|
|
|
7
|
-
#define
|
|
7
|
+
#define DEFAULT_SIZE 4096
|
|
8
8
|
|
|
9
9
|
inline void prepare_io_uring_params(struct io_uring_params *params, uint sqpoll_timeout_msec) {
|
|
10
10
|
memset(params, 0, sizeof(struct io_uring_params));
|
|
@@ -17,17 +17,18 @@ inline void prepare_io_uring_params(struct io_uring_params *params, uint sqpoll_
|
|
|
17
17
|
params->flags |= IORING_SETUP_COOP_TASKRUN;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
void um_setup(VALUE self, struct um *machine, uint
|
|
20
|
+
void um_setup(VALUE self, struct um *machine, uint size, uint sqpoll_timeout_msec) {
|
|
21
21
|
memset(machine, 0, sizeof(struct um));
|
|
22
22
|
|
|
23
23
|
RB_OBJ_WRITE(self, &machine->self, self);
|
|
24
|
+
RB_OBJ_WRITE(self, &machine->pending_fibers, rb_hash_new());
|
|
24
25
|
|
|
25
|
-
machine->
|
|
26
|
+
machine->size = (size > 0) ? size : DEFAULT_SIZE;
|
|
26
27
|
machine->sqpoll_mode = !!sqpoll_timeout_msec;
|
|
27
28
|
|
|
28
29
|
struct io_uring_params params;
|
|
29
30
|
prepare_io_uring_params(¶ms, sqpoll_timeout_msec);
|
|
30
|
-
int ret = io_uring_queue_init_params(machine->
|
|
31
|
+
int ret = io_uring_queue_init_params(machine->size, &machine->ring, ¶ms);
|
|
31
32
|
if (ret) rb_syserr_fail(-ret, strerror(-ret));
|
|
32
33
|
machine->ring_initialized = 1;
|
|
33
34
|
}
|
|
@@ -48,15 +49,16 @@ inline void um_teardown(struct um *machine) {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
inline struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op) {
|
|
51
|
-
|
|
52
|
-
&machine->ring, op
|
|
53
|
-
machine->
|
|
52
|
+
DEBUG_PRINTF("-> %p um_get_sqe: op %p kind=%s unsubmitted=%d pending=%d total=%lu\n",
|
|
53
|
+
&machine->ring, op, um_op_kind_name(op ? op->kind : OP_UNDEFINED),
|
|
54
|
+
machine->metrics.ops_unsubmitted, machine->metrics.ops_pending, machine->metrics.total_ops
|
|
54
55
|
);
|
|
55
56
|
|
|
56
57
|
struct io_uring_sqe *sqe;
|
|
57
58
|
sqe = io_uring_get_sqe(&machine->ring);
|
|
58
59
|
if (likely(sqe)) goto done;
|
|
59
60
|
|
|
61
|
+
fprintf(stderr, "!!!Failed to get SQE\n");
|
|
60
62
|
um_raise_internal_error("Failed to get SQE");
|
|
61
63
|
|
|
62
64
|
// TODO: retry getting SQE?
|
|
@@ -70,10 +72,10 @@ inline struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op) {
|
|
|
70
72
|
done:
|
|
71
73
|
sqe->user_data = (long long)op;
|
|
72
74
|
sqe->flags = 0;
|
|
73
|
-
machine->
|
|
75
|
+
machine->metrics.ops_unsubmitted++;
|
|
74
76
|
if (op) {
|
|
75
|
-
machine->
|
|
76
|
-
machine->
|
|
77
|
+
machine->metrics.ops_pending++;
|
|
78
|
+
machine->metrics.total_ops++;
|
|
77
79
|
}
|
|
78
80
|
return sqe;
|
|
79
81
|
}
|
|
@@ -100,48 +102,48 @@ void *um_submit_without_gvl(void *ptr) {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
inline uint um_submit(struct um *machine) {
|
|
103
|
-
|
|
104
|
-
&machine->ring, machine->
|
|
105
|
+
DEBUG_PRINTF("-> %p um_submit: unsubmitted=%d pending=%d total=%lu\n",
|
|
106
|
+
&machine->ring, machine->metrics.ops_unsubmitted, machine->metrics.ops_pending,
|
|
107
|
+
machine->metrics.total_ops
|
|
105
108
|
);
|
|
106
|
-
if (!machine->
|
|
107
|
-
|
|
108
|
-
&machine->ring
|
|
109
|
-
);
|
|
109
|
+
if (!machine->metrics.ops_unsubmitted) {
|
|
110
|
+
DEBUG_PRINTF("<- %p um_submit: no unsubmitted SQEs, early return\n", &machine->ring);
|
|
110
111
|
return 0;
|
|
111
112
|
}
|
|
112
|
-
|
|
113
|
+
|
|
113
114
|
struct um_submit_ctx ctx = { .machine = machine };
|
|
114
115
|
if (sq_ring_needs_enter(machine))
|
|
115
116
|
rb_thread_call_without_gvl(um_submit_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
|
116
|
-
else
|
|
117
|
+
else
|
|
117
118
|
ctx.result = io_uring_submit(&machine->ring);
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
&machine->ring, ctx.result
|
|
121
|
-
);
|
|
120
|
+
DEBUG_PRINTF("<- %p um_submit: result=%d\n", &machine->ring, ctx.result);
|
|
122
121
|
|
|
123
122
|
if (ctx.result < 0)
|
|
124
123
|
rb_syserr_fail(-ctx.result, strerror(-ctx.result));
|
|
125
124
|
|
|
126
|
-
machine->
|
|
125
|
+
machine->metrics.ops_unsubmitted = 0;
|
|
127
126
|
return ctx.result;
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe) {
|
|
131
130
|
struct um_op *op = (struct um_op *)cqe->user_data;
|
|
132
131
|
if (DEBUG) {
|
|
133
|
-
if (op)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
if (op) {
|
|
133
|
+
DEBUG_PRINTF("<- %p um_process_cqe: op %p kind %s flags %d cqe_res %d cqe_flags %d pending %d\n",
|
|
134
|
+
&machine->ring, op, um_op_kind_name(op->kind), op->flags, cqe->res, cqe->flags, machine->metrics.ops_pending
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
DEBUG_PRINTF("<- %p um_process_cqe: op NULL cqe_res %d cqe_flags %d pending %d\n",
|
|
139
|
+
&machine->ring, cqe->res, cqe->flags, machine->metrics.ops_pending
|
|
140
|
+
);
|
|
141
|
+
}
|
|
139
142
|
}
|
|
140
143
|
if (unlikely(!op)) return;
|
|
141
144
|
|
|
142
|
-
|
|
143
145
|
if (!(cqe->flags & IORING_CQE_F_MORE))
|
|
144
|
-
machine->
|
|
146
|
+
machine->metrics.ops_pending--;
|
|
145
147
|
|
|
146
148
|
if (op->flags & OP_F_FREE_ON_COMPLETE) {
|
|
147
149
|
if (op->flags & OP_F_TRANSIENT)
|
|
@@ -179,8 +181,8 @@ static inline int cq_ring_needs_flush(struct io_uring *ring) {
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
static inline int um_process_ready_cqes(struct um *machine) {
|
|
182
|
-
|
|
183
|
-
&machine->ring, machine->
|
|
184
|
+
DEBUG_PRINTF("-> %p um_process_ready_cqes: unsubmitted=%d pending=%d total=%lu\n",
|
|
185
|
+
&machine->ring, machine->metrics.ops_unsubmitted, machine->metrics.ops_pending, machine->metrics.total_ops
|
|
184
186
|
);
|
|
185
187
|
|
|
186
188
|
unsigned total_count = 0;
|
|
@@ -199,9 +201,9 @@ iterate:
|
|
|
199
201
|
if (overflow_checked) goto done;
|
|
200
202
|
|
|
201
203
|
if (cq_ring_needs_flush(&machine->ring)) {
|
|
202
|
-
|
|
204
|
+
DEBUG_PRINTF("-> %p io_uring_enter\n", &machine->ring);
|
|
203
205
|
int ret = io_uring_enter(machine->ring.ring_fd, 0, 0, IORING_ENTER_GETEVENTS, NULL);
|
|
204
|
-
|
|
206
|
+
DEBUG_PRINTF("<- %p io_uring_enter: result=%d\n", &machine->ring, ret);
|
|
205
207
|
if (ret < 0)
|
|
206
208
|
rb_syserr_fail(-ret, strerror(-ret));
|
|
207
209
|
|
|
@@ -210,9 +212,7 @@ iterate:
|
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
done:
|
|
213
|
-
|
|
214
|
-
&machine->ring, total_count
|
|
215
|
-
);
|
|
215
|
+
DEBUG_PRINTF("<- %p um_process_ready_cqes: total_processed=%u\n", &machine->ring, total_count);
|
|
216
216
|
|
|
217
217
|
return total_count;
|
|
218
218
|
}
|
|
@@ -226,10 +226,10 @@ struct wait_for_cqe_ctx {
|
|
|
226
226
|
|
|
227
227
|
void *um_wait_for_cqe_without_gvl(void *ptr) {
|
|
228
228
|
struct wait_for_cqe_ctx *ctx = ptr;
|
|
229
|
-
if (ctx->machine->
|
|
230
|
-
|
|
231
|
-
&ctx->machine->ring, ctx->machine->
|
|
232
|
-
ctx->machine->
|
|
229
|
+
if (ctx->machine->metrics.ops_unsubmitted) {
|
|
230
|
+
DEBUG_PRINTF("-> %p io_uring_submit_and_wait_timeout: unsubmitted=%d pending=%d total=%lu\n",
|
|
231
|
+
&ctx->machine->ring, ctx->machine->metrics.ops_unsubmitted, ctx->machine->metrics.ops_pending,
|
|
232
|
+
ctx->machine->metrics.total_ops
|
|
233
233
|
);
|
|
234
234
|
|
|
235
235
|
// Attn: The io_uring_submit_and_wait_timeout will not return -EINTR if
|
|
@@ -238,31 +238,49 @@ void *um_wait_for_cqe_without_gvl(void *ptr) {
|
|
|
238
238
|
//
|
|
239
239
|
// https://github.com/axboe/liburing/issues/1280
|
|
240
240
|
int ret = io_uring_submit_and_wait_timeout(&ctx->machine->ring, &ctx->cqe, ctx->wait_nr, NULL, NULL);
|
|
241
|
-
ctx->machine->
|
|
242
|
-
|
|
243
|
-
&ctx->machine->ring, ret
|
|
244
|
-
);
|
|
241
|
+
ctx->machine->metrics.ops_unsubmitted = 0;
|
|
242
|
+
DEBUG_PRINTF("<- %p io_uring_submit_and_wait_timeout: result=%d\n", &ctx->machine->ring, ret);
|
|
245
243
|
ctx->result = (ret > 0 && !ctx->cqe) ? -EINTR : ret;
|
|
246
244
|
}
|
|
247
245
|
else {
|
|
248
|
-
|
|
249
|
-
&ctx->machine->ring, ctx->machine->
|
|
250
|
-
ctx->machine->
|
|
246
|
+
DEBUG_PRINTF("-> %p io_uring_wait_cqes: unsubmitted=%d pending=%d total=%lu\n",
|
|
247
|
+
&ctx->machine->ring, ctx->machine->metrics.ops_unsubmitted, ctx->machine->metrics.ops_pending,
|
|
248
|
+
ctx->machine->metrics.total_ops
|
|
251
249
|
);
|
|
252
250
|
ctx->result = io_uring_wait_cqes(&ctx->machine->ring, &ctx->cqe, ctx->wait_nr, NULL, NULL);
|
|
253
|
-
|
|
254
|
-
&ctx->machine->ring, ctx->result
|
|
255
|
-
);
|
|
251
|
+
DEBUG_PRINTF("<- %p io_uring_wait_cqes: result=%d\n", &ctx->machine->ring, ctx->result);
|
|
256
252
|
}
|
|
257
253
|
return NULL;
|
|
258
254
|
}
|
|
259
255
|
|
|
256
|
+
inline void um_profile_wait_cqe_pre(struct um *machine, double *time_monotonic0, VALUE *fiber) {
|
|
257
|
+
// *fiber = rb_fiber_current();
|
|
258
|
+
*time_monotonic0 = um_get_time_monotonic();
|
|
259
|
+
// double time_cpu = um_get_time_cpu();
|
|
260
|
+
// double elapsed = time_cpu - machine->metrics.time_last_cpu;
|
|
261
|
+
// um_update_fiber_time_run(fiber, time_monotonic0, elapsed);
|
|
262
|
+
// machine->metrics.time_last_cpu = time_cpu;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
inline void um_profile_wait_cqe_post(struct um *machine, double time_monotonic0, VALUE fiber) {
|
|
266
|
+
// double time_cpu = um_get_time_cpu();
|
|
267
|
+
double elapsed = um_get_time_monotonic() - time_monotonic0;
|
|
268
|
+
// um_update_fiber_last_time(fiber, cpu_time1);
|
|
269
|
+
machine->metrics.time_total_wait += elapsed;
|
|
270
|
+
// machine->metrics.time_last_cpu = time_cpu;
|
|
271
|
+
}
|
|
272
|
+
|
|
260
273
|
// Waits for the given minimum number of completion entries. The wait_nr is
|
|
261
274
|
// either 1 - where we wait for at least one CQE to be ready, or 0, where we
|
|
262
275
|
// don't wait, and just process any CQEs that already ready.
|
|
263
276
|
static inline void um_wait_for_and_process_ready_cqes(struct um *machine, int wait_nr) {
|
|
264
277
|
struct wait_for_cqe_ctx ctx = { .machine = machine, .cqe = NULL, .wait_nr = wait_nr };
|
|
278
|
+
machine->metrics.total_waits++;
|
|
279
|
+
double time_monotonic0 = 0.0;
|
|
280
|
+
VALUE fiber;
|
|
281
|
+
if (machine->profile_mode) um_profile_wait_cqe_pre(machine, &time_monotonic0, &fiber);
|
|
265
282
|
rb_thread_call_without_gvl(um_wait_for_cqe_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
|
283
|
+
if (machine->profile_mode) um_profile_wait_cqe_post(machine, time_monotonic0, fiber);
|
|
266
284
|
|
|
267
285
|
if (unlikely(ctx.result < 0)) {
|
|
268
286
|
// the internal calls to (maybe submit) and wait for cqes may fail with:
|
|
@@ -286,22 +304,36 @@ static inline void um_wait_for_and_process_ready_cqes(struct um *machine, int wa
|
|
|
286
304
|
}
|
|
287
305
|
}
|
|
288
306
|
|
|
307
|
+
inline void um_profile_switch(struct um *machine, VALUE next_fiber) {
|
|
308
|
+
// *current_fiber = rb_fiber_current();
|
|
309
|
+
// double time_cpu = um_get_time_cpu();
|
|
310
|
+
// double elapsed = time_cpu - machine->metrics.time_last_cpu;
|
|
311
|
+
// um_update_fiber_time_run(cur_fiber, time_cpu, elapsed);
|
|
312
|
+
// um_update_fiber_time_wait(next_fiber, time_cpu);
|
|
313
|
+
// machine->metrics.time_last_cpu = time_cpu;
|
|
314
|
+
}
|
|
315
|
+
|
|
289
316
|
inline VALUE process_runqueue_op(struct um *machine, struct um_op *op) {
|
|
317
|
+
DEBUG_PRINTF("-> %p process_runqueue_op: op %p\n", &machine->ring, op);
|
|
318
|
+
|
|
319
|
+
machine->metrics.total_switches++;
|
|
290
320
|
VALUE fiber = op->fiber;
|
|
291
321
|
VALUE value = op->value;
|
|
292
322
|
|
|
293
323
|
if (unlikely(op->flags & OP_F_TRANSIENT))
|
|
294
324
|
um_op_free(machine, op);
|
|
295
325
|
|
|
326
|
+
if (machine->profile_mode) um_profile_switch(machine, fiber);
|
|
296
327
|
VALUE ret = rb_fiber_transfer(fiber, 1, &value);
|
|
297
328
|
RB_GC_GUARD(value);
|
|
298
329
|
RB_GC_GUARD(ret);
|
|
299
330
|
return ret;
|
|
300
331
|
}
|
|
301
332
|
|
|
302
|
-
inline VALUE
|
|
303
|
-
|
|
304
|
-
&machine->ring, machine->
|
|
333
|
+
inline VALUE um_switch(struct um *machine) {
|
|
334
|
+
DEBUG_PRINTF("-> %p um_switch: unsubmitted=%d pending=%d total=%lu\n",
|
|
335
|
+
&machine->ring, machine->metrics.ops_unsubmitted, machine->metrics.ops_pending,
|
|
336
|
+
machine->metrics.total_ops
|
|
305
337
|
);
|
|
306
338
|
while (true) {
|
|
307
339
|
struct um_op *op = um_runqueue_shift(machine);
|
|
@@ -316,7 +348,7 @@ inline VALUE um_fiber_switch(struct um *machine) {
|
|
|
316
348
|
// is the op a snooze op and is this the same fiber as the current one?
|
|
317
349
|
if (unlikely(op->kind == OP_SCHEDULE && op->fiber == rb_fiber_current())) {
|
|
318
350
|
// are there any pending ops (i.e. waiting for completion)?
|
|
319
|
-
if (machine->
|
|
351
|
+
if (machine->metrics.ops_pending > 0) {
|
|
320
352
|
// if yes, process completions, get runqueue head, put original op
|
|
321
353
|
// back on runqueue.
|
|
322
354
|
// um_process_ready_cqes(machine);
|
|
@@ -335,6 +367,14 @@ inline VALUE um_fiber_switch(struct um *machine) {
|
|
|
335
367
|
}
|
|
336
368
|
}
|
|
337
369
|
|
|
370
|
+
inline VALUE um_yield(struct um *machine) {
|
|
371
|
+
VALUE fiber = rb_fiber_current();
|
|
372
|
+
rb_hash_aset(machine->pending_fibers, fiber, Qtrue);
|
|
373
|
+
VALUE ret = um_switch(machine);
|
|
374
|
+
rb_hash_delete(machine->pending_fibers, fiber);
|
|
375
|
+
return ret;
|
|
376
|
+
}
|
|
377
|
+
|
|
338
378
|
void um_cancel_op(struct um *machine, struct um_op *op) {
|
|
339
379
|
struct io_uring_sqe *sqe = um_get_sqe(machine, NULL);
|
|
340
380
|
io_uring_prep_cancel64(sqe, (long long)op, 0);
|
|
@@ -342,9 +382,13 @@ void um_cancel_op(struct um *machine, struct um_op *op) {
|
|
|
342
382
|
|
|
343
383
|
inline void um_cancel_and_wait(struct um *machine, struct um_op *op) {
|
|
344
384
|
um_cancel_op(machine, op);
|
|
385
|
+
|
|
386
|
+
VALUE fiber = rb_fiber_current();
|
|
387
|
+
rb_hash_aset(machine->pending_fibers, fiber, Qtrue);
|
|
345
388
|
while (!um_op_completed_p(op)) {
|
|
346
|
-
|
|
389
|
+
um_switch(machine);
|
|
347
390
|
}
|
|
391
|
+
rb_hash_delete(machine->pending_fibers, fiber);
|
|
348
392
|
}
|
|
349
393
|
|
|
350
394
|
inline int um_check_completion(struct um *machine, struct um_op *op) {
|
|
@@ -357,13 +401,6 @@ inline int um_check_completion(struct um *machine, struct um_op *op) {
|
|
|
357
401
|
return 1;
|
|
358
402
|
}
|
|
359
403
|
|
|
360
|
-
inline VALUE um_await(struct um *machine) {
|
|
361
|
-
VALUE ret = um_fiber_switch(machine);
|
|
362
|
-
RAISE_IF_EXCEPTION(ret);
|
|
363
|
-
RB_GC_GUARD(ret);
|
|
364
|
-
return ret;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
404
|
VALUE um_wakeup(struct um *machine) {
|
|
368
405
|
struct io_uring_sqe *sqe = um_get_sqe(machine, NULL);
|
|
369
406
|
io_uring_prep_nop(sqe);
|
|
@@ -449,7 +486,8 @@ VALUE um_sleep(struct um *machine, double duration) {
|
|
|
449
486
|
op.ts = um_double_to_timespec(duration);
|
|
450
487
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
451
488
|
io_uring_prep_timeout(sqe, &op.ts, 0, 0);
|
|
452
|
-
|
|
489
|
+
|
|
490
|
+
VALUE ret = um_yield(machine);
|
|
453
491
|
|
|
454
492
|
if (!um_op_completed_p(&op))
|
|
455
493
|
um_cancel_and_wait(machine, &op);
|
|
@@ -470,7 +508,8 @@ VALUE um_read(struct um *machine, int fd, VALUE buffer, size_t maxlen, ssize_t b
|
|
|
470
508
|
void *ptr = um_prepare_read_buffer(buffer, maxlen, buffer_offset);
|
|
471
509
|
io_uring_prep_read(sqe, fd, ptr, maxlen, file_offset);
|
|
472
510
|
|
|
473
|
-
VALUE ret =
|
|
511
|
+
VALUE ret = um_yield(machine);
|
|
512
|
+
|
|
474
513
|
if (um_check_completion(machine, &op)) {
|
|
475
514
|
um_update_read_buffer(machine, buffer, buffer_offset, op.result.res, op.result.flags);
|
|
476
515
|
ret = INT2NUM(op.result.res);
|
|
@@ -488,10 +527,10 @@ size_t um_read_raw(struct um *machine, int fd, char *buffer, size_t maxlen) {
|
|
|
488
527
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
489
528
|
io_uring_prep_read(sqe, fd, buffer, maxlen, -1);
|
|
490
529
|
|
|
491
|
-
VALUE ret =
|
|
530
|
+
VALUE ret = um_yield(machine);
|
|
531
|
+
|
|
492
532
|
if (um_check_completion(machine, &op)) {
|
|
493
533
|
return op.result.res;
|
|
494
|
-
|
|
495
534
|
}
|
|
496
535
|
|
|
497
536
|
RAISE_IF_EXCEPTION(ret);
|
|
@@ -512,7 +551,8 @@ VALUE um_write(struct um *machine, int fd, VALUE buffer, size_t len, __u64 file_
|
|
|
512
551
|
|
|
513
552
|
io_uring_prep_write(sqe, fd, base, len, file_offset);
|
|
514
553
|
|
|
515
|
-
VALUE ret =
|
|
554
|
+
VALUE ret = um_yield(machine);
|
|
555
|
+
|
|
516
556
|
if (um_check_completion(machine, &op))
|
|
517
557
|
ret = INT2NUM(op.result.res);
|
|
518
558
|
|
|
@@ -530,10 +570,7 @@ VALUE um_write_async(struct um *machine, int fd, VALUE buffer, size_t len, __u64
|
|
|
530
570
|
|
|
531
571
|
struct um_op *op = um_op_alloc(machine);
|
|
532
572
|
um_prep_op(machine, op, OP_WRITE_ASYNC, OP_F_TRANSIENT | OP_F_FREE_ON_COMPLETE);
|
|
533
|
-
RB_OBJ_WRITE(machine->self, &op->fiber, Qnil);
|
|
534
573
|
RB_OBJ_WRITE(machine->self, &op->value, buffer);
|
|
535
|
-
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
536
|
-
|
|
537
574
|
|
|
538
575
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
539
576
|
io_uring_prep_write(sqe, fd, base, len, file_offset);
|
|
@@ -548,7 +585,8 @@ VALUE um_close(struct um *machine, int fd) {
|
|
|
548
585
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
549
586
|
io_uring_prep_close(sqe, fd);
|
|
550
587
|
|
|
551
|
-
VALUE ret =
|
|
588
|
+
VALUE ret = um_yield(machine);
|
|
589
|
+
|
|
552
590
|
if (um_check_completion(machine, &op))
|
|
553
591
|
ret = INT2NUM(fd);
|
|
554
592
|
|
|
@@ -560,9 +598,6 @@ VALUE um_close(struct um *machine, int fd) {
|
|
|
560
598
|
VALUE um_close_async(struct um *machine, int fd) {
|
|
561
599
|
struct um_op *op = um_op_alloc(machine);
|
|
562
600
|
um_prep_op(machine, op, OP_CLOSE_ASYNC, OP_F_FREE_ON_COMPLETE);
|
|
563
|
-
RB_OBJ_WRITE(machine->self, &op->fiber, Qnil);
|
|
564
|
-
RB_OBJ_WRITE(machine->self, &op->value, Qnil);
|
|
565
|
-
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
566
601
|
|
|
567
602
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
568
603
|
io_uring_prep_close(sqe, fd);
|
|
@@ -576,7 +611,8 @@ VALUE um_accept(struct um *machine, int fd) {
|
|
|
576
611
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
577
612
|
io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
|
|
578
613
|
|
|
579
|
-
VALUE ret =
|
|
614
|
+
VALUE ret = um_yield(machine);
|
|
615
|
+
|
|
580
616
|
if (um_check_completion(machine, &op))
|
|
581
617
|
ret = INT2NUM(op.result.res);
|
|
582
618
|
|
|
@@ -591,7 +627,8 @@ VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint fla
|
|
|
591
627
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
592
628
|
io_uring_prep_socket(sqe, domain, type, protocol, flags);
|
|
593
629
|
|
|
594
|
-
VALUE ret =
|
|
630
|
+
VALUE ret = um_yield(machine);
|
|
631
|
+
|
|
595
632
|
if (um_check_completion(machine, &op))
|
|
596
633
|
ret = INT2NUM(op.result.res);
|
|
597
634
|
|
|
@@ -606,7 +643,8 @@ VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, sockle
|
|
|
606
643
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
607
644
|
io_uring_prep_connect(sqe, fd, addr, addrlen);
|
|
608
645
|
|
|
609
|
-
VALUE ret =
|
|
646
|
+
VALUE ret = um_yield(machine);
|
|
647
|
+
|
|
610
648
|
if (um_check_completion(machine, &op))
|
|
611
649
|
ret = INT2NUM(op.result.res);
|
|
612
650
|
|
|
@@ -627,7 +665,8 @@ VALUE um_send(struct um *machine, int fd, VALUE buffer, size_t len, int flags) {
|
|
|
627
665
|
|
|
628
666
|
io_uring_prep_send(sqe, fd, base, len, flags);
|
|
629
667
|
|
|
630
|
-
VALUE ret =
|
|
668
|
+
VALUE ret = um_yield(machine);
|
|
669
|
+
|
|
631
670
|
if (um_check_completion(machine, &op))
|
|
632
671
|
ret = INT2NUM(op.result.res);
|
|
633
672
|
|
|
@@ -647,7 +686,8 @@ VALUE um_send_bundle(struct um *machine, int fd, int bgid, VALUE strings) {
|
|
|
647
686
|
sqe->flags |= IOSQE_BUFFER_SELECT;
|
|
648
687
|
sqe->buf_group = bgid;
|
|
649
688
|
|
|
650
|
-
VALUE ret =
|
|
689
|
+
VALUE ret = um_yield(machine);
|
|
690
|
+
|
|
651
691
|
if (um_check_completion(machine, &op))
|
|
652
692
|
ret = INT2NUM(op.result.res);
|
|
653
693
|
|
|
@@ -664,7 +704,8 @@ VALUE um_recv(struct um *machine, int fd, VALUE buffer, size_t maxlen, int flags
|
|
|
664
704
|
|
|
665
705
|
io_uring_prep_recv(sqe, fd, ptr, maxlen, flags);
|
|
666
706
|
|
|
667
|
-
VALUE ret =
|
|
707
|
+
VALUE ret = um_yield(machine);
|
|
708
|
+
|
|
668
709
|
if (um_check_completion(machine, &op)) {
|
|
669
710
|
um_update_read_buffer(machine, buffer, 0, op.result.res, op.result.flags);
|
|
670
711
|
ret = INT2NUM(op.result.res);
|
|
@@ -681,7 +722,8 @@ VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrl
|
|
|
681
722
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
682
723
|
io_uring_prep_bind(sqe, fd, addr, addrlen);
|
|
683
724
|
|
|
684
|
-
VALUE ret =
|
|
725
|
+
VALUE ret = um_yield(machine);
|
|
726
|
+
|
|
685
727
|
if (um_check_completion(machine, &op))
|
|
686
728
|
ret = INT2NUM(op.result.res);
|
|
687
729
|
|
|
@@ -696,7 +738,8 @@ VALUE um_listen(struct um *machine, int fd, int backlog) {
|
|
|
696
738
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
697
739
|
io_uring_prep_listen(sqe, fd, backlog);
|
|
698
740
|
|
|
699
|
-
VALUE ret =
|
|
741
|
+
VALUE ret = um_yield(machine);
|
|
742
|
+
|
|
700
743
|
if (um_check_completion(machine, &op))
|
|
701
744
|
ret = INT2NUM(op.result.res);
|
|
702
745
|
|
|
@@ -714,7 +757,8 @@ VALUE um_getsockopt(struct um *machine, int fd, int level, int opt) {
|
|
|
714
757
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
715
758
|
io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_GETSOCKOPT, fd, level, opt, &value, sizeof(value));
|
|
716
759
|
|
|
717
|
-
ret =
|
|
760
|
+
ret = um_yield(machine);
|
|
761
|
+
|
|
718
762
|
if (um_check_completion(machine, &op))
|
|
719
763
|
ret = INT2NUM(value);
|
|
720
764
|
|
|
@@ -731,7 +775,8 @@ VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value) {
|
|
|
731
775
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
732
776
|
io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_SETSOCKOPT, fd, level, opt, &value, sizeof(value));
|
|
733
777
|
|
|
734
|
-
ret =
|
|
778
|
+
ret = um_yield(machine);
|
|
779
|
+
|
|
735
780
|
if (um_check_completion(machine, &op))
|
|
736
781
|
ret = INT2NUM(op.result.res);
|
|
737
782
|
|
|
@@ -748,7 +793,8 @@ VALUE um_shutdown(struct um *machine, int fd, int how) {
|
|
|
748
793
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
749
794
|
io_uring_prep_shutdown(sqe, fd, how);
|
|
750
795
|
|
|
751
|
-
ret =
|
|
796
|
+
ret = um_yield(machine);
|
|
797
|
+
|
|
752
798
|
if (um_check_completion(machine, &op))
|
|
753
799
|
ret = INT2NUM(op.result.res);
|
|
754
800
|
|
|
@@ -760,9 +806,6 @@ VALUE um_shutdown(struct um *machine, int fd, int how) {
|
|
|
760
806
|
VALUE um_shutdown_async(struct um *machine, int fd, int how) {
|
|
761
807
|
struct um_op *op = um_op_alloc(machine);
|
|
762
808
|
um_prep_op(machine, op, OP_SHUTDOWN_ASYNC, OP_F_FREE_ON_COMPLETE);
|
|
763
|
-
RB_OBJ_WRITE(machine->self, &op->fiber, Qnil);
|
|
764
|
-
RB_OBJ_WRITE(machine->self, &op->value, Qnil);
|
|
765
|
-
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
766
809
|
|
|
767
810
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
768
811
|
io_uring_prep_shutdown(sqe, fd, how);
|
|
@@ -776,7 +819,8 @@ VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
|
|
|
776
819
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
777
820
|
io_uring_prep_open(sqe, StringValueCStr(pathname), flags, mode);
|
|
778
821
|
|
|
779
|
-
VALUE ret =
|
|
822
|
+
VALUE ret = um_yield(machine);
|
|
823
|
+
|
|
780
824
|
if (um_check_completion(machine, &op))
|
|
781
825
|
ret = INT2NUM(op.result.res);
|
|
782
826
|
|
|
@@ -791,12 +835,15 @@ VALUE um_poll(struct um *machine, int fd, unsigned mask) {
|
|
|
791
835
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
792
836
|
io_uring_prep_poll_add(sqe, fd, mask);
|
|
793
837
|
|
|
794
|
-
VALUE ret =
|
|
838
|
+
VALUE ret = um_yield(machine);
|
|
839
|
+
|
|
795
840
|
if (um_check_completion(machine, &op))
|
|
796
841
|
ret = INT2NUM(op.result.res);
|
|
797
842
|
|
|
798
843
|
RAISE_IF_EXCEPTION(ret);
|
|
799
844
|
RB_GC_GUARD(ret);
|
|
845
|
+
RB_GC_GUARD(op.fiber);
|
|
846
|
+
RB_GC_GUARD(op.value);
|
|
800
847
|
return ret;
|
|
801
848
|
}
|
|
802
849
|
|
|
@@ -822,7 +869,8 @@ VALUE um_select_single(struct um *machine, VALUE rfds, VALUE wfds, VALUE efds, u
|
|
|
822
869
|
prepare_select_poll_ops(machine, &idx, &op, efds, efds_len, OP_F_SELECT_POLLPRI, POLLPRI);
|
|
823
870
|
assert(idx == 1);
|
|
824
871
|
|
|
825
|
-
VALUE ret =
|
|
872
|
+
VALUE ret = um_yield(machine);
|
|
873
|
+
|
|
826
874
|
um_check_completion(machine, &op);
|
|
827
875
|
RAISE_IF_EXCEPTION(ret);
|
|
828
876
|
|
|
@@ -854,7 +902,7 @@ VALUE um_select(struct um *machine, VALUE rfds, VALUE wfds, VALUE efds) {
|
|
|
854
902
|
prepare_select_poll_ops(machine, &idx, ops, efds, efds_len, OP_F_SELECT_POLLPRI, POLLPRI);
|
|
855
903
|
assert(idx == total_len);
|
|
856
904
|
|
|
857
|
-
VALUE ret =
|
|
905
|
+
VALUE ret = um_yield(machine);
|
|
858
906
|
if (unlikely(um_value_is_exception_p(ret))) {
|
|
859
907
|
free(ops);
|
|
860
908
|
um_raise_exception(ret);
|
|
@@ -916,7 +964,8 @@ VALUE um_waitid(struct um *machine, int idtype, int id, int options) {
|
|
|
916
964
|
siginfo_t infop;
|
|
917
965
|
io_uring_prep_waitid(sqe, idtype, id, &infop, options, 0);
|
|
918
966
|
|
|
919
|
-
VALUE ret =
|
|
967
|
+
VALUE ret = um_yield(machine);
|
|
968
|
+
|
|
920
969
|
if (um_check_completion(machine, &op))
|
|
921
970
|
ret = INT2NUM(op.result.res);
|
|
922
971
|
|
|
@@ -937,7 +986,7 @@ VALUE um_waitid_status(struct um *machine, int idtype, int id, int options) {
|
|
|
937
986
|
siginfo_t infop;
|
|
938
987
|
io_uring_prep_waitid(sqe, idtype, id, &infop, options | WNOWAIT, 0);
|
|
939
988
|
|
|
940
|
-
VALUE ret =
|
|
989
|
+
VALUE ret = um_yield(machine);
|
|
941
990
|
if (um_check_completion(machine, &op))
|
|
942
991
|
ret = INT2NUM(op.result.res);
|
|
943
992
|
|
|
@@ -983,7 +1032,8 @@ VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned in
|
|
|
983
1032
|
memset(&stat, 0, sizeof(stat));
|
|
984
1033
|
io_uring_prep_statx(sqe, dirfd, path_ptr, flags, mask, &stat);
|
|
985
1034
|
|
|
986
|
-
VALUE ret =
|
|
1035
|
+
VALUE ret = um_yield(machine);
|
|
1036
|
+
|
|
987
1037
|
if (um_check_completion(machine, &op))
|
|
988
1038
|
ret = INT2NUM(op.result.res);
|
|
989
1039
|
|
|
@@ -1003,7 +1053,7 @@ VALUE accept_each_start(VALUE arg) {
|
|
|
1003
1053
|
io_uring_prep_multishot_accept(sqe, ctx->fd, NULL, NULL, 0);
|
|
1004
1054
|
|
|
1005
1055
|
while (true) {
|
|
1006
|
-
VALUE ret =
|
|
1056
|
+
VALUE ret = um_yield(ctx->machine);
|
|
1007
1057
|
if (!um_op_completed_p(ctx->op)) {
|
|
1008
1058
|
RAISE_IF_EXCEPTION(ret);
|
|
1009
1059
|
return ret;
|
|
@@ -1045,6 +1095,8 @@ VALUE multishot_complete(VALUE arg) {
|
|
|
1045
1095
|
if (ctx->read_buf)
|
|
1046
1096
|
free(ctx->read_buf);
|
|
1047
1097
|
|
|
1098
|
+
rb_hash_delete(ctx->machine->pending_fibers, ctx->op->fiber);
|
|
1099
|
+
|
|
1048
1100
|
return Qnil;
|
|
1049
1101
|
}
|
|
1050
1102
|
|
|
@@ -1067,7 +1119,7 @@ int um_read_each_singleshot_loop(struct op_ctx *ctx) {
|
|
|
1067
1119
|
struct io_uring_sqe *sqe = um_get_sqe(ctx->machine, ctx->op);
|
|
1068
1120
|
io_uring_prep_read(sqe, ctx->fd, ctx->read_buf, ctx->read_maxlen, -1);
|
|
1069
1121
|
|
|
1070
|
-
VALUE ret =
|
|
1122
|
+
VALUE ret = um_yield(ctx->machine);
|
|
1071
1123
|
if (um_op_completed_p(ctx->op)) {
|
|
1072
1124
|
um_raise_on_error_result(ctx->op->result.res);
|
|
1073
1125
|
if (!ctx->op->result.res) return total;
|
|
@@ -1128,7 +1180,7 @@ VALUE read_recv_each_start(VALUE arg) {
|
|
|
1128
1180
|
int total = 0;
|
|
1129
1181
|
|
|
1130
1182
|
while (true) {
|
|
1131
|
-
VALUE ret =
|
|
1183
|
+
VALUE ret = um_yield(ctx->machine);
|
|
1132
1184
|
if (!um_op_completed_p(ctx->op)) {
|
|
1133
1185
|
RAISE_IF_EXCEPTION(ret);
|
|
1134
1186
|
return ret;
|
|
@@ -1179,7 +1231,7 @@ VALUE periodically_start(VALUE arg) {
|
|
|
1179
1231
|
io_uring_prep_timeout(sqe, &ctx->ts, 0, IORING_TIMEOUT_MULTISHOT);
|
|
1180
1232
|
|
|
1181
1233
|
while (true) {
|
|
1182
|
-
VALUE ret =
|
|
1234
|
+
VALUE ret = um_switch(ctx->machine);
|
|
1183
1235
|
if (!um_op_completed_p(ctx->op)) {
|
|
1184
1236
|
RAISE_IF_EXCEPTION(ret);
|
|
1185
1237
|
return ret;
|
|
@@ -1215,3 +1267,40 @@ VALUE um_periodically(struct um *machine, double interval) {
|
|
|
1215
1267
|
struct op_ctx ctx = { .machine = machine, .op = &op, .ts = op.ts, .read_buf = NULL };
|
|
1216
1268
|
return rb_ensure(periodically_start, (VALUE)&ctx, multishot_complete, (VALUE)&ctx);
|
|
1217
1269
|
}
|
|
1270
|
+
|
|
1271
|
+
extern VALUE SYM_size;
|
|
1272
|
+
extern VALUE SYM_total_ops;
|
|
1273
|
+
extern VALUE SYM_total_switches;
|
|
1274
|
+
extern VALUE SYM_total_waits;
|
|
1275
|
+
extern VALUE SYM_ops_pending;
|
|
1276
|
+
extern VALUE SYM_ops_unsubmitted;
|
|
1277
|
+
extern VALUE SYM_ops_runqueue;
|
|
1278
|
+
extern VALUE SYM_ops_free;
|
|
1279
|
+
extern VALUE SYM_ops_transient;
|
|
1280
|
+
extern VALUE SYM_time_total_cpu;
|
|
1281
|
+
extern VALUE SYM_time_total_wait;
|
|
1282
|
+
|
|
1283
|
+
VALUE um_metrics(struct um *machine, struct um_metrics *metrics) {
|
|
1284
|
+
VALUE hash = rb_hash_new();
|
|
1285
|
+
|
|
1286
|
+
rb_hash_aset(hash, SYM_size, UINT2NUM(machine->size));
|
|
1287
|
+
|
|
1288
|
+
rb_hash_aset(hash, SYM_total_ops, ULONG2NUM(metrics->total_ops));
|
|
1289
|
+
rb_hash_aset(hash, SYM_total_switches, ULONG2NUM(metrics->total_switches));
|
|
1290
|
+
rb_hash_aset(hash, SYM_total_waits, ULONG2NUM(metrics->total_waits));
|
|
1291
|
+
|
|
1292
|
+
rb_hash_aset(hash, SYM_ops_pending, UINT2NUM(metrics->ops_pending));
|
|
1293
|
+
rb_hash_aset(hash, SYM_ops_unsubmitted, UINT2NUM(metrics->ops_unsubmitted));
|
|
1294
|
+
rb_hash_aset(hash, SYM_ops_runqueue, UINT2NUM(metrics->ops_runqueue));
|
|
1295
|
+
rb_hash_aset(hash, SYM_ops_free, UINT2NUM(metrics->ops_free));
|
|
1296
|
+
rb_hash_aset(hash, SYM_ops_transient, UINT2NUM(metrics->ops_transient));
|
|
1297
|
+
|
|
1298
|
+
if (machine->profile_mode) {
|
|
1299
|
+
double total_cpu = um_get_time_cpu() - metrics->time_first_cpu;
|
|
1300
|
+
rb_hash_aset(hash, SYM_time_total_cpu, DBL2NUM(total_cpu));
|
|
1301
|
+
rb_hash_aset(hash, SYM_time_total_wait, DBL2NUM(metrics->time_total_wait));
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
return hash;
|
|
1305
|
+
RB_GC_GUARD(hash);
|
|
1306
|
+
}
|