uringmachine 0.3 → 0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +85 -0
  4. data/TODO.md +5 -0
  5. data/examples/echo_server.rb +18 -40
  6. data/examples/inout.rb +19 -0
  7. data/examples/nc.rb +36 -0
  8. data/ext/um/extconf.rb +6 -15
  9. data/ext/um/um.c +245 -53
  10. data/ext/um/um.h +21 -9
  11. data/ext/um/um_class.c +74 -87
  12. data/ext/um/um_const.c +184 -0
  13. data/ext/um/um_op.c +10 -13
  14. data/ext/um/um_utils.c +48 -3
  15. data/lib/uringmachine/version.rb +1 -1
  16. data/lib/uringmachine.rb +12 -0
  17. data/test/helper.rb +8 -0
  18. data/test/test_um.rb +227 -7
  19. data/vendor/liburing/.github/workflows/build.yml +29 -1
  20. data/vendor/liburing/.gitignore +1 -0
  21. data/vendor/liburing/CHANGELOG +15 -0
  22. data/vendor/liburing/CONTRIBUTING.md +165 -0
  23. data/vendor/liburing/configure +32 -0
  24. data/vendor/liburing/examples/Makefile +8 -1
  25. data/vendor/liburing/examples/kdigest.c +405 -0
  26. data/vendor/liburing/examples/proxy.c +75 -8
  27. data/vendor/liburing/liburing.pc.in +1 -1
  28. data/vendor/liburing/src/Makefile +16 -2
  29. data/vendor/liburing/src/include/liburing/io_uring.h +31 -0
  30. data/vendor/liburing/src/include/liburing/sanitize.h +39 -0
  31. data/vendor/liburing/src/include/liburing.h +31 -4
  32. data/vendor/liburing/src/liburing-ffi.map +5 -0
  33. data/vendor/liburing/src/liburing.map +1 -0
  34. data/vendor/liburing/src/queue.c +3 -0
  35. data/vendor/liburing/src/register.c +36 -0
  36. data/vendor/liburing/src/sanitize.c +176 -0
  37. data/vendor/liburing/src/setup.c +1 -1
  38. data/vendor/liburing/test/35fa71a030ca.c +7 -0
  39. data/vendor/liburing/test/500f9fbadef8.c +2 -0
  40. data/vendor/liburing/test/7ad0e4b2f83c.c +0 -25
  41. data/vendor/liburing/test/917257daa0fe.c +7 -0
  42. data/vendor/liburing/test/Makefile +31 -4
  43. data/vendor/liburing/test/a0908ae19763.c +7 -0
  44. data/vendor/liburing/test/a4c0b3decb33.c +7 -0
  45. data/vendor/liburing/test/accept.c +14 -4
  46. data/vendor/liburing/test/b19062a56726.c +7 -0
  47. data/vendor/liburing/test/bind-listen.c +2 -2
  48. data/vendor/liburing/test/buf-ring-nommap.c +10 -3
  49. data/vendor/liburing/test/buf-ring.c +2 -0
  50. data/vendor/liburing/test/coredump.c +7 -0
  51. data/vendor/liburing/test/cq-overflow.c +13 -1
  52. data/vendor/liburing/test/d4ae271dfaae.c +11 -3
  53. data/vendor/liburing/test/defer-taskrun.c +2 -2
  54. data/vendor/liburing/test/defer-tw-timeout.c +4 -1
  55. data/vendor/liburing/test/defer.c +2 -2
  56. data/vendor/liburing/test/double-poll-crash.c +1 -1
  57. data/vendor/liburing/test/eeed8b54e0df.c +2 -0
  58. data/vendor/liburing/test/eventfd.c +0 -1
  59. data/vendor/liburing/test/exit-no-cleanup.c +11 -0
  60. data/vendor/liburing/test/fadvise.c +9 -26
  61. data/vendor/liburing/test/fdinfo.c +9 -1
  62. data/vendor/liburing/test/file-register.c +14 -2
  63. data/vendor/liburing/test/file-update.c +1 -1
  64. data/vendor/liburing/test/file-verify.c +27 -16
  65. data/vendor/liburing/test/files-exit-hang-timeout.c +1 -2
  66. data/vendor/liburing/test/fixed-buf-iter.c +3 -1
  67. data/vendor/liburing/test/fixed-hugepage.c +12 -1
  68. data/vendor/liburing/test/fsnotify.c +1 -0
  69. data/vendor/liburing/test/futex.c +16 -4
  70. data/vendor/liburing/test/helpers.c +47 -0
  71. data/vendor/liburing/test/helpers.h +6 -0
  72. data/vendor/liburing/test/init-mem.c +5 -3
  73. data/vendor/liburing/test/io-cancel.c +0 -24
  74. data/vendor/liburing/test/io_uring_passthrough.c +2 -0
  75. data/vendor/liburing/test/io_uring_register.c +25 -6
  76. data/vendor/liburing/test/iopoll-leak.c +4 -0
  77. data/vendor/liburing/test/iopoll-overflow.c +1 -1
  78. data/vendor/liburing/test/iopoll.c +3 -3
  79. data/vendor/liburing/test/kallsyms.c +203 -0
  80. data/vendor/liburing/test/link-timeout.c +159 -0
  81. data/vendor/liburing/test/linked-defer-close.c +224 -0
  82. data/vendor/liburing/test/madvise.c +12 -25
  83. data/vendor/liburing/test/min-timeout-wait.c +0 -25
  84. data/vendor/liburing/test/min-timeout.c +0 -25
  85. data/vendor/liburing/test/mkdir.c +6 -0
  86. data/vendor/liburing/test/msg-ring.c +8 -2
  87. data/vendor/liburing/test/napi-test.c +15 -2
  88. data/vendor/liburing/test/no-mmap-inval.c +2 -0
  89. data/vendor/liburing/test/nop.c +44 -0
  90. data/vendor/liburing/test/ooo-file-unreg.c +1 -1
  91. data/vendor/liburing/test/open-close.c +40 -0
  92. data/vendor/liburing/test/openat2.c +37 -14
  93. data/vendor/liburing/test/poll-many.c +13 -7
  94. data/vendor/liburing/test/poll-mshot-update.c +17 -10
  95. data/vendor/liburing/test/poll-v-poll.c +6 -3
  96. data/vendor/liburing/test/pollfree.c +148 -0
  97. data/vendor/liburing/test/read-mshot-empty.c +156 -153
  98. data/vendor/liburing/test/read-mshot.c +276 -27
  99. data/vendor/liburing/test/read-write.c +78 -13
  100. data/vendor/liburing/test/recv-msgall-stream.c +3 -0
  101. data/vendor/liburing/test/recv-msgall.c +5 -0
  102. data/vendor/liburing/test/recvsend_bundle-inc.c +680 -0
  103. data/vendor/liburing/test/recvsend_bundle.c +92 -29
  104. data/vendor/liburing/test/reg-fd-only.c +14 -4
  105. data/vendor/liburing/test/regbuf-clone.c +187 -0
  106. data/vendor/liburing/test/regbuf-merge.c +7 -0
  107. data/vendor/liburing/test/register-restrictions.c +86 -85
  108. data/vendor/liburing/test/rename.c +59 -1
  109. data/vendor/liburing/test/ringbuf-read.c +5 -0
  110. data/vendor/liburing/test/ringbuf-status.c +5 -1
  111. data/vendor/liburing/test/runtests.sh +16 -1
  112. data/vendor/liburing/test/send-zerocopy.c +59 -0
  113. data/vendor/liburing/test/short-read.c +1 -0
  114. data/vendor/liburing/test/socket.c +43 -0
  115. data/vendor/liburing/test/splice.c +3 -1
  116. data/vendor/liburing/test/sq-poll-dup.c +1 -1
  117. data/vendor/liburing/test/sq-poll-share.c +2 -0
  118. data/vendor/liburing/test/sqpoll-disable-exit.c +8 -0
  119. data/vendor/liburing/test/sqpoll-exit-hang.c +1 -25
  120. data/vendor/liburing/test/sqpoll-sleep.c +1 -25
  121. data/vendor/liburing/test/statx.c +89 -0
  122. data/vendor/liburing/test/stdout.c +2 -0
  123. data/vendor/liburing/test/submit-and-wait.c +1 -25
  124. data/vendor/liburing/test/submit-reuse.c +4 -26
  125. data/vendor/liburing/test/symlink.c +12 -1
  126. data/vendor/liburing/test/sync-cancel.c +48 -21
  127. data/vendor/liburing/test/thread-exit.c +5 -0
  128. data/vendor/liburing/test/timeout-new.c +1 -26
  129. data/vendor/liburing/test/timeout.c +12 -26
  130. data/vendor/liburing/test/unlink.c +94 -1
  131. data/vendor/liburing/test/uring_cmd_ublk.c +1252 -0
  132. data/vendor/liburing/test/waitid.c +62 -8
  133. data/vendor/liburing/test/wq-aff.c +35 -0
  134. data/vendor/liburing/test/xfail_prep_link_timeout_out_of_scope.c +46 -0
  135. data/vendor/liburing/test/xfail_register_buffers_out_of_scope.c +51 -0
  136. metadata +17 -4
  137. data/examples/event_loop.rb +0 -69
  138. data/examples/fibers.rb +0 -105
data/ext/um/um.c CHANGED
@@ -1,6 +1,5 @@
1
1
  #include "um.h"
2
2
  #include "ruby/thread.h"
3
- #include <sys/mman.h>
4
3
 
5
4
  void um_setup(struct um *machine) {
6
5
  machine->ring_initialized = 0;
@@ -13,7 +12,7 @@ void um_setup(struct um *machine) {
13
12
  machine->result_freelist = NULL;
14
13
 
15
14
  unsigned prepared_limit = 4096;
16
- int flags = 0;
15
+ unsigned flags = 0;
17
16
  #ifdef HAVE_IORING_SETUP_SUBMIT_ALL
18
17
  flags |= IORING_SETUP_SUBMIT_ALL;
19
18
  #endif
@@ -98,6 +97,7 @@ inline void um_handle_submitted_op_cqe_single(struct um *machine, struct um_op *
98
97
 
99
98
  inline void um_handle_submitted_op_cqe_multi(struct um *machine, struct um_op *op, struct io_uring_cqe *cqe) {
100
99
  if (!op->results_head) {
100
+ // if no results are ready yet, schedule the corresponding fiber
101
101
  struct um_op *op2 = um_op_checkout(machine);
102
102
  op2->state = OP_schedule;
103
103
  op2->fiber = op->fiber;
@@ -114,16 +114,19 @@ inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe) {
114
114
  struct um_op *op = (struct um_op *)cqe->user_data;
115
115
  if (unlikely(!op)) return;
116
116
 
117
+ // if (op->is_multishot)
118
+ // printf("process_cqe %p state: %d result: %d flags: %d (%d)\n", op, op->state, cqe->res, cqe->flags, (cqe->flags & IORING_CQE_F_MORE));
119
+
117
120
  switch (op->state) {
118
121
  case OP_submitted:
119
122
  if (unlikely(cqe->res == -ECANCELED)) {
120
123
  um_op_checkin(machine, op);
121
124
  break;
122
125
  }
123
- if (!op->is_multishot)
124
- um_handle_submitted_op_cqe_single(machine, op, cqe);
125
- else
126
+ if (op->is_multishot)
126
127
  um_handle_submitted_op_cqe_multi(machine, op, cqe);
128
+ else
129
+ um_handle_submitted_op_cqe_single(machine, op, cqe);
127
130
  break;
128
131
  case OP_abandonned:
129
132
  // op has been abandonned by the I/O method, so we need to cleanup (check
@@ -196,7 +199,7 @@ loop:
196
199
  // we need to submit events and check completions without blocking
197
200
  if (
198
201
  unlikely(
199
- first_iteration && machine->unsubmitted_count &&
202
+ first_iteration && machine->unsubmitted_count &&
200
203
  machine->runqueue_head &&
201
204
  machine->runqueue_head->fiber == rb_fiber_current()
202
205
  )
@@ -209,9 +212,9 @@ loop:
209
212
  op = um_runqueue_shift(machine);
210
213
  if (op) {
211
214
  VALUE resume_value = op->resume_value;
212
- if (op->state == OP_schedule) {
215
+ if (op->state == OP_schedule)
213
216
  um_op_checkin(machine, op);
214
- }
217
+
215
218
  // the resume value is disregarded, we pass the fiber itself
216
219
  VALUE v = rb_fiber_transfer(op->fiber, 1, &resume_value);
217
220
  return v;
@@ -226,7 +229,7 @@ static inline void um_cancel_op(struct um *machine, struct um_op *op) {
226
229
  io_uring_prep_cancel64(sqe, (long long)op, 0);
227
230
  }
228
231
 
229
- static inline VALUE um_await_op(struct um *machine, struct um_op *op, int *result, int *flags) {
232
+ static inline VALUE um_await_op(struct um *machine, struct um_op *op, __s32 *result, __u32 *flags) {
230
233
  op->fiber = rb_fiber_current();
231
234
  VALUE v = um_fiber_switch(machine);
232
235
  int is_exception = um_value_is_exception_p(v);
@@ -240,8 +243,6 @@ static inline VALUE um_await_op(struct um *machine, struct um_op *op, int *resul
240
243
  // checked in.
241
244
  if (result) *result = op->cqe_result;
242
245
  if (flags) *flags = op->cqe_flags;
243
- if (!op->is_multishot)
244
- um_op_checkin(machine, op);
245
246
  }
246
247
 
247
248
  if (unlikely(is_exception)) um_raise_exception(v);
@@ -279,7 +280,11 @@ inline void um_interrupt(struct um *machine, VALUE fiber, VALUE value) {
279
280
  struct op_ensure_ctx {
280
281
  struct um *machine;
281
282
  struct um_op *op;
283
+ int fd;
282
284
  int bgid;
285
+
286
+ void *read_buf;
287
+ int read_maxlen;
283
288
  };
284
289
 
285
290
  VALUE um_timeout_ensure(VALUE arg) {
@@ -291,10 +296,10 @@ VALUE um_timeout_ensure(VALUE arg) {
291
296
  um_cancel_op(ctx->machine, ctx->op);
292
297
  ctx->op->state == OP_abandonned;
293
298
  }
294
- else {
299
+ else
295
300
  // completed, so can be checked in
296
301
  um_op_checkin(ctx->machine, ctx->op);
297
- }
302
+
298
303
  return Qnil;
299
304
  }
300
305
 
@@ -315,23 +320,31 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
315
320
  return rb_ensure(rb_yield, Qnil, um_timeout_ensure, (VALUE)&ctx);
316
321
  }
317
322
 
323
+ inline void discard_op_if_completed(struct um *machine, struct um_op *op) {
324
+ if (op->state == OP_completed) um_op_checkin(machine, op);
325
+ }
326
+
318
327
  inline VALUE um_sleep(struct um *machine, double duration) {
319
328
  struct um_op *op = um_op_checkout(machine);
320
329
  op->ts = um_double_to_timespec(duration);
321
330
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
322
- int result = 0;
331
+ __s32 result = 0;
323
332
 
324
333
  io_uring_prep_timeout(sqe, &op->ts, 0, 0);
325
334
  op->state = OP_submitted;
326
335
 
327
- return um_await_op(machine, op, &result, NULL);
336
+ um_await_op(machine, op, &result, NULL);
337
+
338
+ discard_op_if_completed(machine, op);
339
+ if (result != -ETIME) um_raise_on_system_error(result);
340
+ return Qnil;
328
341
  }
329
342
 
330
343
  inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
331
344
  struct um_op *op = um_op_checkout(machine);
332
345
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
333
- int result = 0;
334
- int flags = 0;
346
+ __s32 result = 0;
347
+ __u32 flags = 0;
335
348
 
336
349
  void *ptr = um_prepare_read_buffer(buffer, maxlen, buffer_offset);
337
350
  io_uring_prep_read(sqe, fd, ptr, maxlen, -1);
@@ -339,6 +352,7 @@ inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int b
339
352
 
340
353
  um_await_op(machine, op, &result, &flags);
341
354
 
355
+ discard_op_if_completed(machine, op);
342
356
  um_raise_on_system_error(result);
343
357
  um_update_read_buffer(machine, buffer, buffer_offset, result, flags);
344
358
  return INT2FIX(result);
@@ -346,6 +360,7 @@ inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int b
346
360
 
347
361
  VALUE um_multishot_ensure(VALUE arg) {
348
362
  struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
363
+
349
364
  switch (ctx->op->state) {
350
365
  case OP_submitted:
351
366
  um_cancel_op(ctx->machine, ctx->op);
@@ -353,59 +368,141 @@ VALUE um_multishot_ensure(VALUE arg) {
353
368
  case OP_completed:
354
369
  um_op_checkin(ctx->machine, ctx->op);
355
370
  break;
356
- default:
371
+ default:
357
372
  }
373
+
374
+ if (ctx->read_buf) free(ctx->read_buf);
358
375
  return Qnil;
359
376
  }
360
377
 
378
+ static inline void um_read_each_prepare_op(struct op_ensure_ctx *ctx, int singleshot_mode) {
379
+ struct um_op *op = um_op_checkout(ctx->machine);
380
+ struct io_uring_sqe *sqe = um_get_sqe(ctx->machine, op);
381
+
382
+ if (singleshot_mode)
383
+ io_uring_prep_read(sqe, ctx->fd, ctx->read_buf, ctx->read_maxlen, -1);
384
+ else {
385
+ io_uring_prep_read_multishot(sqe, ctx->fd, 0, -1, ctx->bgid);
386
+ op->is_multishot = 1;
387
+ }
388
+
389
+ op->state = OP_submitted;
390
+ ctx->op = op;
391
+ }
392
+
393
+ int um_read_each_safe_loop_singleshot(struct op_ensure_ctx *ctx, int total) {
394
+ struct buf_ring_descriptor *desc = ctx->machine->buffer_rings + ctx->bgid;
395
+ __s32 result = 0;
396
+ ctx->read_maxlen = desc->buf_size;
397
+ ctx->read_buf = malloc(desc->buf_size);
398
+
399
+ while (1) {
400
+ um_read_each_prepare_op(ctx, 1);
401
+ um_await_op(ctx->machine, ctx->op, &result, NULL);
402
+ um_raise_on_system_error(result);
403
+ if (!result) return total;
404
+
405
+ total += result;
406
+ VALUE buf = rb_str_new(ctx->read_buf, result);
407
+ rb_yield(buf);
408
+ um_op_checkin(ctx->machine, ctx->op);
409
+ }
410
+ }
411
+
412
+
413
+
414
+ int um_read_each_multishot_process_results(struct op_ensure_ctx *ctx, int *total) {
415
+ __s32 result = 0;
416
+ __u32 flags = 0;
417
+ __s32 bad_result = 0;
418
+ int eof = 0;
419
+
420
+ while (um_op_result_shift(ctx->machine, ctx->op, &result, &flags)) {
421
+ if (result < 0) {
422
+ bad_result = result;
423
+ break;
424
+ }
425
+ if (result == 0) {
426
+ eof = 1;
427
+ break;
428
+ }
429
+
430
+ *total += result;
431
+ VALUE buf = um_get_string_from_buffer_ring(ctx->machine, ctx->bgid, result, flags);
432
+ rb_yield(buf);
433
+ }
434
+
435
+ if (ctx->op->state == OP_completed) {
436
+ um_op_checkin(ctx->machine, ctx->op);
437
+
438
+ // TTY devices might not support multishot reads:
439
+ // https://github.com/axboe/liburing/issues/1185. A workaround is to
440
+ // fallback to singleshot mode, using the first buffer in the buffer
441
+ // group.
442
+ if (!(flags & IORING_CQE_F_BUFFER)) {
443
+ *total = um_read_each_safe_loop_singleshot(ctx, *total);
444
+ return 0;
445
+ }
446
+ else
447
+ um_read_each_prepare_op(ctx, 0);
448
+ }
449
+ if (bad_result)
450
+ um_raise_on_system_error(bad_result);
451
+
452
+ return eof ? 0 : 1;
453
+ }
454
+
361
455
  VALUE um_read_each_safe_loop(VALUE arg) {
362
456
  struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
363
- int result = 0;
364
- int flags = 0;
365
457
  int total = 0;
366
458
 
459
+ um_read_each_prepare_op(ctx, 0);
460
+
367
461
  while (1) {
368
462
  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
- }
463
+ if (!ctx->op->results_head)
464
+ rb_raise(rb_eRuntimeError, "no result found!\n");
465
+
466
+ if (!um_read_each_multishot_process_results(ctx, &total))
467
+ return INT2NUM(total);
382
468
  }
383
469
  }
384
470
 
385
471
  VALUE um_read_each(struct um *machine, int fd, int bgid) {
472
+ struct op_ensure_ctx ctx = { .machine = machine, .fd = fd, .bgid = bgid, .read_buf = NULL };
473
+ return rb_ensure(um_read_each_safe_loop, (VALUE)&ctx, um_multishot_ensure, (VALUE)&ctx);
474
+ }
475
+
476
+ VALUE um_write(struct um *machine, int fd, VALUE buffer, int len) {
386
477
  struct um_op *op = um_op_checkout(machine);
387
478
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
479
+ __s32 result = 0;
480
+ __u32 flags = 0;
388
481
 
389
- op->is_multishot = 1;
390
- io_uring_prep_read_multishot(sqe, fd, 0, -1, bgid);
482
+ io_uring_prep_write(sqe, fd, RSTRING_PTR(buffer), len, -1);
391
483
  op->state = OP_submitted;
392
484
 
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);
485
+ um_await_op(machine, op, &result, &flags);
486
+
487
+ discard_op_if_completed(machine, op);
488
+ um_raise_on_system_error(result);
489
+ return INT2FIX(result);
395
490
  }
396
491
 
397
- VALUE um_write(struct um *machine, int fd, VALUE buffer, int len) {
492
+ VALUE um_close(struct um *machine, int fd) {
398
493
  struct um_op *op = um_op_checkout(machine);
399
494
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
400
- int result = 0;
401
- int flags = 0;
495
+ __s32 result = 0;
496
+ __u32 flags = 0;
402
497
 
403
- io_uring_prep_write(sqe, fd, RSTRING_PTR(buffer), len, -1);
498
+ io_uring_prep_close(sqe, fd);
404
499
  op->state = OP_submitted;
405
500
 
406
501
  um_await_op(machine, op, &result, &flags);
502
+
503
+ discard_op_if_completed(machine, op);
407
504
  um_raise_on_system_error(result);
408
- return INT2FIX(result);
505
+ return INT2FIX(fd);
409
506
  }
410
507
 
411
508
  VALUE um_accept(struct um *machine, int fd) {
@@ -413,28 +510,33 @@ VALUE um_accept(struct um *machine, int fd) {
413
510
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
414
511
  struct sockaddr addr;
415
512
  socklen_t len;
416
- int result = 0;
417
- int flags = 0;
513
+ __s32 result = 0;
514
+ __u32 flags = 0;
515
+
418
516
  io_uring_prep_accept(sqe, fd, &addr, &len, 0);
419
517
  op->state = OP_submitted;
420
518
 
421
519
  um_await_op(machine, op, &result, &flags);
520
+
521
+ discard_op_if_completed(machine, op);
422
522
  um_raise_on_system_error(result);
423
523
  return INT2FIX(result);
424
524
  }
425
525
 
426
526
  VALUE um_accept_each_safe_loop(VALUE arg) {
427
527
  struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
428
- int result = 0;
429
- int flags = 0;
528
+ __s32 result = 0;
529
+ __u32 flags = 0;
430
530
 
431
531
  while (1) {
432
- um_await_op(ctx->machine, ctx->op, NULL, NULL);
532
+ um_await_op(ctx->machine, ctx->op, &result, &flags);
433
533
  if (!ctx->op->results_head) {
434
- // TODO: raise, this shouldn't happen
435
- printf("no result found!\n");
534
+ // this shouldn't happen!
535
+ rb_raise(rb_eRuntimeError, "no result found for accept_each loop");
436
536
  }
537
+
437
538
  while (um_op_result_shift(ctx->machine, ctx->op, &result, &flags)) {
539
+ um_raise_on_system_error(result);
438
540
  if (likely(result > 0))
439
541
  rb_yield(INT2FIX(result));
440
542
  else
@@ -446,12 +548,102 @@ VALUE um_accept_each_safe_loop(VALUE arg) {
446
548
  VALUE um_accept_each(struct um *machine, int fd) {
447
549
  struct um_op *op = um_op_checkout(machine);
448
550
  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);
551
+ io_uring_prep_multishot_accept(sqe, fd, NULL, NULL, 0);
452
552
  op->state = OP_submitted;
453
553
  op->is_multishot = 1;
454
554
 
455
- struct op_ensure_ctx ctx = { .machine = machine, .op = op };
555
+ struct op_ensure_ctx ctx = { .machine = machine, .op = op, .read_buf = NULL };
456
556
  return rb_ensure(um_accept_each_safe_loop, (VALUE)&ctx, um_multishot_ensure, (VALUE)&ctx);
457
557
  }
558
+
559
+ VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint flags) {
560
+ struct um_op *op = um_op_checkout(machine);
561
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
562
+ int result = 0;
563
+
564
+ io_uring_prep_socket(sqe, domain, type, protocol, flags);
565
+ op->state = OP_submitted;
566
+
567
+ um_await_op(machine, op, &result, NULL);
568
+
569
+ discard_op_if_completed(machine, op);
570
+ um_raise_on_system_error(result);
571
+ return INT2FIX(result);
572
+ }
573
+
574
+ VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, socklen_t addrlen) {
575
+ struct um_op *op = um_op_checkout(machine);
576
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
577
+ int result = 0;
578
+
579
+ io_uring_prep_connect(sqe, fd, addr, addrlen);
580
+ op->state = OP_submitted;
581
+
582
+ um_await_op(machine, op, &result, NULL);
583
+
584
+ discard_op_if_completed(machine, op);
585
+ um_raise_on_system_error(result);
586
+ return INT2FIX(result);
587
+ }
588
+
589
+ VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags) {
590
+ struct um_op *op = um_op_checkout(machine);
591
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
592
+ int result = 0;
593
+
594
+ io_uring_prep_send(sqe, fd, RSTRING_PTR(buffer), len, flags);
595
+ op->state = OP_submitted;
596
+
597
+ um_await_op(machine, op, &result, NULL);
598
+
599
+ discard_op_if_completed(machine, op);
600
+ um_raise_on_system_error(result);
601
+ return INT2FIX(result);
602
+ }
603
+
604
+ VALUE um_recv(struct um *machine, int fd, VALUE buffer, int maxlen, int flags) {
605
+ struct um_op *op = um_op_checkout(machine);
606
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
607
+ int result = 0;
608
+
609
+ void *ptr = um_prepare_read_buffer(buffer, maxlen, 0);
610
+ io_uring_prep_recv(sqe, fd, ptr, maxlen, flags);
611
+ op->state = OP_submitted;
612
+
613
+ um_await_op(machine, op, &result, NULL);
614
+
615
+ discard_op_if_completed(machine, op);
616
+ um_raise_on_system_error(result);
617
+ um_update_read_buffer(machine, buffer, 0, result, flags);
618
+ return INT2FIX(result);
619
+ }
620
+
621
+ VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrlen) {
622
+ struct um_op *op = um_op_checkout(machine);
623
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
624
+ int result = 0;
625
+
626
+ io_uring_prep_bind(sqe, fd, addr, addrlen);
627
+ op->state = OP_submitted;
628
+
629
+ um_await_op(machine, op, &result, NULL);
630
+
631
+ discard_op_if_completed(machine, op);
632
+ um_raise_on_system_error(result);
633
+ return INT2FIX(result);
634
+ }
635
+
636
+ VALUE um_listen(struct um *machine, int fd, int backlog) {
637
+ struct um_op *op = um_op_checkout(machine);
638
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
639
+ int result = 0;
640
+
641
+ io_uring_prep_listen(sqe, fd, backlog);
642
+ op->state = OP_submitted;
643
+
644
+ um_await_op(machine, op, &result, NULL);
645
+
646
+ discard_op_if_completed(machine, op);
647
+ um_raise_on_system_error(result);
648
+ return INT2FIX(result);
649
+ }
data/ext/um/um.h CHANGED
@@ -32,8 +32,8 @@ enum op_state {
32
32
  struct um_result_entry {
33
33
  struct um_result_entry *next;
34
34
 
35
- int result;
36
- int flags;
35
+ __s32 result;
36
+ __u32 flags;
37
37
  };
38
38
 
39
39
  struct um_op {
@@ -44,12 +44,12 @@ struct um_op {
44
44
  // linked list for multishot results
45
45
  struct um_result_entry *results_head;
46
46
  struct um_result_entry *results_tail;
47
-
47
+
48
48
  VALUE fiber;
49
49
  VALUE resume_value;
50
50
  int is_multishot;
51
51
  struct __kernel_timespec ts;
52
-
52
+
53
53
  int cqe_result;
54
54
  int cqe_flags;
55
55
  };
@@ -59,7 +59,8 @@ struct buf_ring_descriptor {
59
59
  size_t br_size;
60
60
  unsigned buf_count;
61
61
  unsigned buf_size;
62
- char *buf_base;
62
+ unsigned buf_mask;
63
+ void *buf_base;
63
64
  };
64
65
 
65
66
  #define BUFFER_RING_MAX_COUNT 10
@@ -94,16 +95,18 @@ VALUE um_raise_exception(VALUE v);
94
95
  void um_raise_on_system_error(int result);
95
96
 
96
97
  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);
98
+ void um_update_read_buffer(struct um *machine, VALUE buffer, int buffer_offset, __s32 result, __u32 flags);
99
+
100
+ int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count);
101
+ VALUE um_get_string_from_buffer_ring(struct um *machine, int bgid, __s32 result, __u32 flags);
99
102
 
100
103
  VALUE um_fiber_switch(struct um *machine);
101
104
  VALUE um_await(struct um *machine);
102
105
 
103
106
  void um_op_checkin(struct um *machine, struct um_op *op);
104
107
  struct um_op* um_op_checkout(struct um *machine);
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);
108
+ void um_op_result_push(struct um *machine, struct um_op *op, __s32 result, __u32 flags);
109
+ int um_op_result_shift(struct um *machine, struct um_op *op, __s32 *result, __u32 *flags);
107
110
 
108
111
  struct um_op *um_runqueue_find_by_fiber(struct um *machine, VALUE fiber);
109
112
  void um_runqueue_push(struct um *machine, struct um_op *op);
@@ -118,8 +121,17 @@ VALUE um_sleep(struct um *machine, double duration);
118
121
  VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset);
119
122
  VALUE um_read_each(struct um *machine, int fd, int bgid);
120
123
  VALUE um_write(struct um *machine, int fd, VALUE buffer, int len);
124
+ VALUE um_close(struct um *machine, int fd);
121
125
 
122
126
  VALUE um_accept(struct um *machine, int fd);
123
127
  VALUE um_accept_each(struct um *machine, int fd);
128
+ VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint flags);
129
+ VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, socklen_t addrlen);
130
+ VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags);
131
+ VALUE um_recv(struct um *machine, int fd, VALUE buffer, int maxlen, int flags);
132
+ VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrlen);
133
+ VALUE um_listen(struct um *machine, int fd, int backlog);
134
+
135
+ void um_define_net_constants(VALUE mod);
124
136
 
125
137
  #endif // UM_H