uringmachine 0.12.1 → 0.14

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 540e2e4df0b1953f58e36ab4dea2024a793b1bea4fd2358f64733f003b192510
4
- data.tar.gz: 463cf782db4604358e1f54270e24e4bb97ee92176cafdb36079fd9d4a5132fda
3
+ metadata.gz: 38a9b75f18075a31aa179cc63b67e7e80b75b3797a701e1b2164831f4c20c352
4
+ data.tar.gz: 50432253c8aca2713b7eeaf8ae69c72c1e4efece1c85285076cf91c97011a887
5
5
  SHA512:
6
- metadata.gz: 9e54c0f81a6a6a0523a5210a00e5bd7d3a7c0598d8e15c4434080429ae10328079a06d1075fab88894eb2c6ac827e8c929b4fbc18a2bdc7cf20d7efda0d1bd81
7
- data.tar.gz: 9de7ecd460344ec496fc061edb15a9fadd8b9a5b81db0ca8844afd6cc81bb79b7881af54a008b3c43565725f9234ea66728e69c7eaa6a09d090a0977128e0326
6
+ metadata.gz: c7426118f51b6b3460d7ec46cc6e64e8a3aaa50e2552d2c44f718cfa79a9624310d9d68148f030be5a03582abf3fe520389e7a27fb814cff141c78f327f4f76a
7
+ data.tar.gz: a2e0ce93138435f99f84956ba82004fe06b4c7f530c37aa6585d998f2a4f6c448eac406ef0c7c602335bc776c5d40b543288f6e5cf16b1fe8bab1141fe7569c6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 2025-06-09 Version 0.14
2
+
3
+ - Add `#close_async`, `#shutdown_async`
4
+
5
+ # 2025-06-07 Version 0.13
6
+
7
+ - Add `#write_async`
8
+
1
9
  # 2025-06-07 Version 0.12.1
2
10
 
3
11
  - Improve portability of `UM` constants
data/ext/um/um.c CHANGED
@@ -72,6 +72,11 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
72
72
  // op, op->kind, op->flags, cqe->res, cqe->flags, machine->pending_count
73
73
  // );
74
74
 
75
+ if (op->flags & OP_F_FREE_ON_COMPLETE) {
76
+ um_op_free(machine, op);
77
+ return;
78
+ }
79
+
75
80
  if (unlikely((cqe->res == -ECANCELED) && (op->flags & OP_F_IGNORE_CANCELED))) return;
76
81
 
77
82
  op->flags |= OP_F_COMPLETED;
@@ -247,20 +252,14 @@ inline VALUE um_await(struct um *machine) {
247
252
  return raise_if_exception(v);
248
253
  }
249
254
 
250
- inline void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind) {
255
+ inline void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind, unsigned flags) {
251
256
  memset(op, 0, sizeof(struct um_op));
252
257
  op->kind = kind;
253
- switch (kind) {
254
- case OP_ACCEPT_MULTISHOT:
255
- case OP_READ_MULTISHOT:
256
- case OP_RECV_MULTISHOT:
257
- case OP_TIMEOUT_MULTISHOT:
258
- case OP_SLEEP_MULTISHOT:
259
- op->flags |= OP_F_MULTISHOT;
260
- default:
261
- }
262
- RB_OBJ_WRITE(machine->self, &op->fiber, rb_fiber_current());
263
- op->value = Qnil;
258
+ op->flags = flags;
259
+
260
+ VALUE fiber = (flags & OP_F_FREE_ON_COMPLETE) ? Qnil : rb_fiber_current();
261
+ RB_OBJ_WRITE(machine->self, &op->fiber, fiber);
262
+ RB_OBJ_WRITE(machine->self, &op->value, Qnil);
264
263
  }
265
264
 
266
265
  inline void um_schedule(struct um *machine, VALUE fiber, VALUE value) {
@@ -302,7 +301,7 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
302
301
  if (!ID_new) ID_new = rb_intern("new");
303
302
 
304
303
  struct um_op *op = um_op_alloc(machine);
305
- um_prep_op(machine, op, OP_TIMEOUT);
304
+ um_prep_op(machine, op, OP_TIMEOUT, 0);
306
305
  op->ts = um_double_to_timespec(NUM2DBL(interval));
307
306
  RB_OBJ_WRITE(machine->self, &op->fiber, rb_fiber_current());
308
307
  RB_OBJ_WRITE(machine->self, &op->value, rb_funcall(class, ID_new, 0));
@@ -320,7 +319,7 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
320
319
 
321
320
  VALUE um_sleep(struct um *machine, double duration) {
322
321
  struct um_op op;
323
- um_prep_op(machine, &op, OP_SLEEP);
322
+ um_prep_op(machine, &op, OP_SLEEP, 0);
324
323
  op.ts = um_double_to_timespec(duration);
325
324
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
326
325
  io_uring_prep_timeout(sqe, &op.ts, 0, 0);
@@ -339,7 +338,7 @@ VALUE um_sleep(struct um *machine, double duration) {
339
338
 
340
339
  inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
341
340
  struct um_op op;
342
- um_prep_op(machine, &op, OP_READ);
341
+ um_prep_op(machine, &op, OP_READ, 0);
343
342
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
344
343
  void *ptr = um_prepare_read_buffer(buffer, maxlen, buffer_offset);
345
344
  io_uring_prep_read(sqe, fd, ptr, maxlen, -1);
@@ -358,7 +357,7 @@ inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int b
358
357
 
359
358
  inline size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen) {
360
359
  struct um_op op;
361
- um_prep_op(machine, &op, OP_READ);
360
+ um_prep_op(machine, &op, OP_READ, 0);
362
361
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
363
362
  io_uring_prep_read(sqe, fd, buffer, maxlen, -1);
364
363
 
@@ -374,7 +373,7 @@ inline size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen)
374
373
 
375
374
  VALUE um_write(struct um *machine, int fd, VALUE str, int len) {
376
375
  struct um_op op;
377
- um_prep_op(machine, &op, OP_WRITE);
376
+ um_prep_op(machine, &op, OP_WRITE, 0);
378
377
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
379
378
  const int str_len = RSTRING_LEN(str);
380
379
  if (len > str_len) len = str_len;
@@ -390,9 +389,19 @@ VALUE um_write(struct um *machine, int fd, VALUE str, int len) {
390
389
  return raise_if_exception(ret);
391
390
  }
392
391
 
392
+ VALUE um_write_async(struct um *machine, int fd, VALUE str) {
393
+ struct um_op *op = um_op_alloc(machine);
394
+ um_prep_op(machine, op, OP_WRITE_ASYNC, OP_F_FREE_ON_COMPLETE);
395
+ RB_OBJ_WRITE(machine->self, &op->value, str);
396
+
397
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
398
+ io_uring_prep_write(sqe, fd, RSTRING_PTR(str), RSTRING_LEN(str), -1);
399
+ return str;
400
+ }
401
+
393
402
  VALUE um_close(struct um *machine, int fd) {
394
403
  struct um_op op;
395
- um_prep_op(machine, &op, OP_CLOSE);
404
+ um_prep_op(machine, &op, OP_CLOSE, 0);
396
405
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
397
406
  io_uring_prep_close(sqe, fd);
398
407
 
@@ -404,9 +413,19 @@ VALUE um_close(struct um *machine, int fd) {
404
413
  return raise_if_exception(ret);
405
414
  }
406
415
 
416
+ VALUE um_close_async(struct um *machine, int fd) {
417
+ struct um_op *op = um_op_alloc(machine);
418
+ um_prep_op(machine, op, OP_CLOSE_ASYNC, OP_F_FREE_ON_COMPLETE);
419
+
420
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
421
+ io_uring_prep_close(sqe, fd);
422
+
423
+ return INT2NUM(fd);
424
+ }
425
+
407
426
  VALUE um_accept(struct um *machine, int fd) {
408
427
  struct um_op op;
409
- um_prep_op(machine, &op, OP_ACCEPT);
428
+ um_prep_op(machine, &op, OP_ACCEPT, 0);
410
429
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
411
430
  io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
412
431
 
@@ -420,7 +439,7 @@ VALUE um_accept(struct um *machine, int fd) {
420
439
 
421
440
  VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint flags) {
422
441
  struct um_op op;
423
- um_prep_op(machine, &op, OP_SOCKET);
442
+ um_prep_op(machine, &op, OP_SOCKET, 0);
424
443
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
425
444
  io_uring_prep_socket(sqe, domain, type, protocol, flags);
426
445
 
@@ -434,7 +453,7 @@ VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint fla
434
453
 
435
454
  VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, socklen_t addrlen) {
436
455
  struct um_op op;
437
- um_prep_op(machine, &op, OP_CONNECT);
456
+ um_prep_op(machine, &op, OP_CONNECT, 0);
438
457
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
439
458
  io_uring_prep_connect(sqe, fd, addr, addrlen);
440
459
 
@@ -448,7 +467,7 @@ VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, sockle
448
467
 
449
468
  VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags) {
450
469
  struct um_op op;
451
- um_prep_op(machine, &op, OP_SEND);
470
+ um_prep_op(machine, &op, OP_SEND, 0);
452
471
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
453
472
  io_uring_prep_send(sqe, fd, RSTRING_PTR(buffer), len, flags);
454
473
 
@@ -463,7 +482,7 @@ VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags) {
463
482
 
464
483
  VALUE um_recv(struct um *machine, int fd, VALUE buffer, int maxlen, int flags) {
465
484
  struct um_op op;
466
- um_prep_op(machine, &op, OP_RECV);
485
+ um_prep_op(machine, &op, OP_RECV, 0);
467
486
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
468
487
  void *ptr = um_prepare_read_buffer(buffer, maxlen, 0);
469
488
  io_uring_prep_recv(sqe, fd, ptr, maxlen, flags);
@@ -481,7 +500,7 @@ VALUE um_recv(struct um *machine, int fd, VALUE buffer, int maxlen, int flags) {
481
500
 
482
501
  VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrlen) {
483
502
  struct um_op op;
484
- um_prep_op(machine, &op, OP_BIND);
503
+ um_prep_op(machine, &op, OP_BIND, 0);
485
504
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
486
505
  io_uring_prep_bind(sqe, fd, addr, addrlen);
487
506
 
@@ -495,7 +514,7 @@ VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrl
495
514
 
496
515
  VALUE um_listen(struct um *machine, int fd, int backlog) {
497
516
  struct um_op op;
498
- um_prep_op(machine, &op, OP_BIND);
517
+ um_prep_op(machine, &op, OP_BIND, 0);
499
518
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
500
519
  io_uring_prep_listen(sqe, fd, backlog);
501
520
 
@@ -513,7 +532,7 @@ VALUE um_getsockopt(struct um *machine, int fd, int level, int opt) {
513
532
 
514
533
  #ifdef HAVE_IO_URING_PREP_CMD_SOCK
515
534
  struct um_op op;
516
- um_prep_op(machine, &op, OP_GETSOCKOPT);
535
+ um_prep_op(machine, &op, OP_GETSOCKOPT, 0);
517
536
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
518
537
  io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_GETSOCKOPT, fd, level, opt, &value, sizeof(value));
519
538
 
@@ -537,7 +556,7 @@ VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value) {
537
556
 
538
557
  #ifdef HAVE_IO_URING_PREP_CMD_SOCK
539
558
  struct um_op op;
540
- um_prep_op(machine, &op, OP_SETSOCKOPT);
559
+ um_prep_op(machine, &op, OP_SETSOCKOPT, 0);
541
560
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
542
561
  io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_SETSOCKOPT, fd, level, opt, &value, sizeof(value));
543
562
 
@@ -559,7 +578,7 @@ VALUE um_shutdown(struct um *machine, int fd, int how) {
559
578
  VALUE ret = Qnil;
560
579
 
561
580
  struct um_op op;
562
- um_prep_op(machine, &op, OP_SHUTDOWN);
581
+ um_prep_op(machine, &op, OP_SHUTDOWN, 0);
563
582
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
564
583
  io_uring_prep_shutdown(sqe, fd, how);
565
584
 
@@ -571,9 +590,19 @@ VALUE um_shutdown(struct um *machine, int fd, int how) {
571
590
  return raise_if_exception(ret);
572
591
  }
573
592
 
593
+ VALUE um_shutdown_async(struct um *machine, int fd, int how) {
594
+ struct um_op *op = um_op_alloc(machine);
595
+ um_prep_op(machine, op, OP_SHUTDOWN_ASYNC, OP_F_FREE_ON_COMPLETE);
596
+
597
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
598
+ io_uring_prep_shutdown(sqe, fd, how);
599
+
600
+ return INT2NUM(fd);
601
+ }
602
+
574
603
  VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
575
604
  struct um_op op;
576
- um_prep_op(machine, &op, OP_OPEN);
605
+ um_prep_op(machine, &op, OP_OPEN, 0);
577
606
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
578
607
  io_uring_prep_open(sqe, StringValueCStr(pathname), flags, mode);
579
608
 
@@ -587,7 +616,7 @@ VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
587
616
 
588
617
  VALUE um_waitpid(struct um *machine, int pid, int options) {
589
618
  struct um_op op;
590
- um_prep_op(machine, &op, OP_WAITPID);
619
+ um_prep_op(machine, &op, OP_WAITPID, 0);
591
620
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
592
621
 
593
622
  siginfo_t infop;
@@ -628,7 +657,7 @@ VALUE statx_to_hash(struct statx *stat) {
628
657
 
629
658
  VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask) {
630
659
  struct um_op op;
631
- um_prep_op(machine, &op, OP_STATX);
660
+ um_prep_op(machine, &op, OP_STATX, 0);
632
661
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
633
662
 
634
663
  struct statx stat;
@@ -703,7 +732,7 @@ VALUE multishot_complete(VALUE arg) {
703
732
 
704
733
  VALUE um_accept_each(struct um *machine, int fd) {
705
734
  struct um_op op;
706
- um_prep_op(machine, &op, OP_ACCEPT_MULTISHOT);
735
+ um_prep_op(machine, &op, OP_ACCEPT_MULTISHOT, OP_F_MULTISHOT);
707
736
 
708
737
  struct op_ctx ctx = { .machine = machine, .op = &op, .fd = fd, .read_buf = NULL };
709
738
  return rb_ensure(accept_each_start, (VALUE)&ctx, multishot_complete, (VALUE)&ctx);
@@ -716,7 +745,7 @@ int um_read_each_singleshot_loop(struct op_ctx *ctx) {
716
745
  int total = 0;
717
746
 
718
747
  while (1) {
719
- um_prep_op(ctx->machine, ctx->op, OP_READ);
748
+ um_prep_op(ctx->machine, ctx->op, OP_READ, 0);
720
749
  struct io_uring_sqe *sqe = um_get_sqe(ctx->machine, ctx->op);
721
750
  io_uring_prep_read(sqe, ctx->fd, ctx->read_buf, ctx->read_maxlen, -1);
722
751
 
@@ -806,7 +835,7 @@ VALUE read_recv_each_start(VALUE arg) {
806
835
 
807
836
  VALUE um_read_each(struct um *machine, int fd, int bgid) {
808
837
  struct um_op op;
809
- um_prep_op(machine, &op, OP_READ_MULTISHOT);
838
+ um_prep_op(machine, &op, OP_READ_MULTISHOT, OP_F_MULTISHOT);
810
839
 
811
840
  struct op_ctx ctx = { .machine = machine, .op = &op, .fd = fd, .bgid = bgid, .read_buf = NULL };
812
841
  return rb_ensure(read_recv_each_start, (VALUE)&ctx, multishot_complete, (VALUE)&ctx);
@@ -814,7 +843,7 @@ VALUE um_read_each(struct um *machine, int fd, int bgid) {
814
843
 
815
844
  VALUE um_recv_each(struct um *machine, int fd, int bgid, int flags) {
816
845
  struct um_op op;
817
- um_prep_op(machine, &op, OP_RECV_MULTISHOT);
846
+ um_prep_op(machine, &op, OP_RECV_MULTISHOT, OP_F_MULTISHOT);
818
847
 
819
848
  struct op_ctx ctx = { .machine = machine, .op = &op, .fd = fd, .bgid = bgid, .read_buf = NULL, .flags = flags };
820
849
  return rb_ensure(read_recv_each_start, (VALUE)&ctx, multishot_complete, (VALUE)&ctx);
@@ -853,7 +882,7 @@ VALUE periodically_start(VALUE arg) {
853
882
 
854
883
  VALUE um_periodically(struct um *machine, double interval) {
855
884
  struct um_op op;
856
- um_prep_op(machine, &op, OP_SLEEP_MULTISHOT);
885
+ um_prep_op(machine, &op, OP_SLEEP_MULTISHOT, OP_F_MULTISHOT);
857
886
  op.ts = um_double_to_timespec(interval);
858
887
 
859
888
  struct op_ctx ctx = { .machine = machine, .op = &op, .ts = op.ts, .read_buf = NULL };
data/ext/um/um.h CHANGED
@@ -28,7 +28,9 @@ enum op_kind {
28
28
  OP_OPEN,
29
29
  OP_READ,
30
30
  OP_WRITE,
31
+ OP_WRITE_ASYNC,
31
32
  OP_CLOSE,
33
+ OP_CLOSE_ASYNC,
32
34
  OP_STATX,
33
35
 
34
36
  OP_ACCEPT,
@@ -41,6 +43,7 @@ enum op_kind {
41
43
  OP_GETSOCKOPT,
42
44
  OP_SETSOCKOPT,
43
45
  OP_SHUTDOWN,
46
+ OP_SHUTDOWN_ASYNC,
44
47
 
45
48
  OP_WAITPID,
46
49
 
@@ -59,6 +62,7 @@ enum op_kind {
59
62
  #define OP_F_ASYNC (1U << 2) // op belongs to an AsyncOp
60
63
  #define OP_F_IGNORE_CANCELED (1U << 3) // CQE with -ECANCEL should be ignored
61
64
  #define OP_F_MULTISHOT (1U << 4) // op is multishot
65
+ #define OP_F_FREE_ON_COMPLETE (1U << 5) // op should be freed on receiving CQE
62
66
 
63
67
  struct um_op_result {
64
68
  __s32 res;
@@ -200,7 +204,7 @@ VALUE um_raise_exception(VALUE v);
200
204
 
201
205
  #define raise_if_exception(v) (um_value_is_exception_p(v) ? um_raise_exception(v) : v)
202
206
 
203
- void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind);
207
+ void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind, unsigned flags);
204
208
  void um_raise_on_error_result(int result);
205
209
  void * um_prepare_read_buffer(VALUE buffer, unsigned len, int ofs);
206
210
  void um_update_read_buffer(struct um *machine, VALUE buffer, int buffer_offset, __s32 result, __u32 flags);
@@ -227,9 +231,11 @@ size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen);
227
231
  VALUE um_read_each(struct um *machine, int fd, int bgid);
228
232
  VALUE um_write(struct um *machine, int fd, VALUE str, int len);
229
233
  VALUE um_close(struct um *machine, int fd);
234
+ VALUE um_close_async(struct um *machine, int fd);
230
235
  VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode);
231
236
  VALUE um_waitpid(struct um *machine, int pid, int options);
232
237
  VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask);
238
+ VALUE um_write_async(struct um *machine, int fd, VALUE str);
233
239
 
234
240
  VALUE um_accept(struct um *machine, int fd);
235
241
  VALUE um_accept_each(struct um *machine, int fd);
@@ -243,6 +249,7 @@ VALUE um_listen(struct um *machine, int fd, int backlog);
243
249
  VALUE um_getsockopt(struct um *machine, int fd, int level, int opt);
244
250
  VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value);
245
251
  VALUE um_shutdown(struct um *machine, int fd, int how);
252
+ VALUE um_shutdown_async(struct um *machine, int fd, int how);
246
253
 
247
254
  void um_async_op_set(VALUE self, struct um *machine, struct um_op *op);
248
255
  VALUE um_async_op_await(struct um_async_op *async_op);
data/ext/um/um_async_op.c CHANGED
@@ -6,9 +6,8 @@ VALUE um_prep_timeout(struct um *machine, double interval) {
6
6
  if (!ID_new) ID_new = rb_intern("new");
7
7
 
8
8
  struct um_op *op = malloc(sizeof(struct um_op));
9
- um_prep_op(machine, op, OP_TIMEOUT);
9
+ um_prep_op(machine, op, OP_TIMEOUT, OP_F_TRANSIENT | OP_F_ASYNC);
10
10
  op->ts = um_double_to_timespec(interval);
11
- op->flags = OP_F_TRANSIENT | OP_F_ASYNC;
12
11
 
13
12
  VALUE obj = rb_funcall(cAsyncOp, rb_intern_const("new"), 0);
14
13
  um_async_op_set(obj, machine, op);
data/ext/um/um_class.c CHANGED
@@ -131,6 +131,11 @@ VALUE UM_write(int argc, VALUE *argv, VALUE self) {
131
131
  return um_write(machine, NUM2INT(fd), buffer, bytes);
132
132
  }
133
133
 
134
+ VALUE UM_write_async(VALUE self, VALUE fd, VALUE str) {
135
+ struct um *machine = um_get_machine(self);
136
+ return um_write_async(machine, NUM2INT(fd), str);
137
+ }
138
+
134
139
  VALUE UM_statx(VALUE self, VALUE dirfd, VALUE path, VALUE flags, VALUE mask) {
135
140
  struct um *machine = um_get_machine(self);
136
141
  return um_statx(machine, NUM2INT(dirfd), path, NUM2INT(flags), NUM2UINT(mask));
@@ -141,6 +146,11 @@ VALUE UM_close(VALUE self, VALUE fd) {
141
146
  return um_close(machine, NUM2INT(fd));
142
147
  }
143
148
 
149
+ VALUE UM_close_async(VALUE self, VALUE fd) {
150
+ struct um *machine = um_get_machine(self);
151
+ return um_close_async(machine, NUM2INT(fd));
152
+ }
153
+
144
154
  VALUE UM_accept(VALUE self, VALUE fd) {
145
155
  struct um *machine = um_get_machine(self);
146
156
  return um_accept(machine, NUM2INT(fd));
@@ -159,7 +169,11 @@ VALUE UM_socket(VALUE self, VALUE domain, VALUE type, VALUE protocol, VALUE flag
159
169
  VALUE UM_shutdown(VALUE self, VALUE fd, VALUE how) {
160
170
  struct um *machine = um_get_machine(self);
161
171
  return um_shutdown(machine, NUM2INT(fd), NUM2INT(how));
172
+ }
162
173
 
174
+ VALUE UM_shutdown_async(VALUE self, VALUE fd, VALUE how) {
175
+ struct um *machine = um_get_machine(self);
176
+ return um_shutdown_async(machine, NUM2INT(fd), NUM2INT(how));
163
177
  }
164
178
 
165
179
  VALUE UM_connect(VALUE self, VALUE fd, VALUE host, VALUE port) {
@@ -342,12 +356,14 @@ void Init_UM(void) {
342
356
  rb_define_method(cUM, "yield", UM_yield, 0);
343
357
 
344
358
  rb_define_method(cUM, "close", UM_close, 1);
359
+ rb_define_method(cUM, "close_async", UM_close_async, 1);
345
360
  rb_define_method(cUM, "open", UM_open, 2);
346
361
  rb_define_method(cUM, "read", UM_read, -1);
347
362
  rb_define_method(cUM, "read_each", UM_read_each, 2);
348
363
  rb_define_method(cUM, "sleep", UM_sleep, 1);
349
364
  rb_define_method(cUM, "periodically", UM_periodically, 1);
350
365
  rb_define_method(cUM, "write", UM_write, -1);
366
+ rb_define_method(cUM, "write_async", UM_write_async, 2);
351
367
  rb_define_method(cUM, "statx", UM_statx, 4);
352
368
 
353
369
  rb_define_method(cUM, "waitpid", UM_waitpid, 2);
@@ -364,6 +380,7 @@ void Init_UM(void) {
364
380
  rb_define_method(cUM, "setsockopt", UM_setsockopt, 4);
365
381
  rb_define_method(cUM, "socket", UM_socket, 4);
366
382
  rb_define_method(cUM, "shutdown", UM_shutdown, 2);
383
+ rb_define_method(cUM, "shutdown_async", UM_shutdown_async, 2);
367
384
 
368
385
  rb_define_method(cUM, "prep_timeout", UM_prep_timeout, 1);
369
386
 
data/ext/um/um_sync.c CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  void um_futex_wait(struct um *machine, uint32_t *futex, uint32_t expect) {
8
8
  struct um_op op;
9
- um_prep_op(machine, &op, OP_FUTEX_WAIT);
9
+ um_prep_op(machine, &op, OP_FUTEX_WAIT, 0);
10
10
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
11
11
  io_uring_prep_futex_wait(
12
12
  sqe, (uint32_t *)futex, expect, FUTEX_BITSET_MATCH_ANY,
@@ -27,7 +27,7 @@ void um_futex_wait(struct um *machine, uint32_t *futex, uint32_t expect) {
27
27
 
28
28
  void um_futex_wake(struct um *machine, uint32_t *futex, uint32_t num_waiters) {
29
29
  struct um_op op;
30
- um_prep_op(machine, &op, OP_FUTEX_WAKE);
30
+ um_prep_op(machine, &op, OP_FUTEX_WAKE, 0);
31
31
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
32
32
  // submit futex_wait
33
33
  io_uring_prep_futex_wake(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.12.1'
4
+ VERSION = '0.14'
5
5
  end
data/lib/uringmachine.rb CHANGED
@@ -35,14 +35,15 @@ class UringMachine
35
35
  f.add_done_listener(queue)
36
36
  end
37
37
  end
38
- return results.values if !pending
39
-
40
- while !pending.empty?
41
- f = self.shift(queue)
42
- pending.delete(f)
43
- results[f] = f.result
38
+ if pending
39
+ while !pending.empty?
40
+ f = self.shift(queue)
41
+ pending.delete(f)
42
+ results[f] = f.result
43
+ end
44
44
  end
45
- results.values
45
+ values = results.values
46
+ fibers.size == 1 ? values.first : values
46
47
  end
47
48
 
48
49
  def resolve(hostname, type = :A)
data/test/helper.rb CHANGED
@@ -74,4 +74,20 @@ class UMBaseTest < Minitest::Test
74
74
  @@port += 1
75
75
  end
76
76
  end
77
+
78
+ def make_socket_pair
79
+ port = 10000 + rand(30000)
80
+ server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
81
+ @machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
82
+ @machine.bind(server_fd, '127.0.0.1', port)
83
+ @machine.listen(server_fd, UM::SOMAXCONN)
84
+
85
+ client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
86
+ @machine.connect(client_conn_fd, '127.0.0.1', port)
87
+
88
+ server_conn_fd = @machine.accept(server_fd)
89
+
90
+ @machine.close(server_fd)
91
+ [client_conn_fd, server_conn_fd]
92
+ end
77
93
  end
data/test/test_fiber.rb CHANGED
@@ -88,7 +88,7 @@ class FiberJoinTest < UMBaseTest
88
88
  res = machine.join(f)
89
89
  assert_equal 0, x
90
90
  assert_equal 3, machine.shift(q)
91
- assert_equal [42], res
91
+ assert_equal 42, res
92
92
  end
93
93
 
94
94
  def test_join_multiple
@@ -125,7 +125,7 @@ class FiberJoinTest < UMBaseTest
125
125
  f = machine.shift(q)
126
126
  assert_kind_of Fiber, f
127
127
  res = machine.join(f)
128
- assert_equal [:foo], res
128
+ assert_equal :foo, res
129
129
  ensure
130
130
  t2.join
131
131
  end
@@ -135,8 +135,7 @@ class FiberJoinTest < UMBaseTest
135
135
  raise "Foobar"
136
136
  end
137
137
 
138
- res = machine.join(f)
139
- e = res.first
138
+ e = machine.join(f)
140
139
  assert_kind_of RuntimeError, e
141
140
  assert_equal 'Foobar', e.message
142
141
  end
data/test/test_um.rb CHANGED
@@ -25,6 +25,12 @@ class SpinTest < UMBaseTest
25
25
 
26
26
  assert_equal :foo, x
27
27
  end
28
+
29
+ def test_spin_with_unhandled_exception
30
+ f = machine.spin { raise 'foo' }
31
+ result = machine.join(f)
32
+ assert_kind_of RuntimeError, result
33
+ end
28
34
  end
29
35
 
30
36
  class SnoozeTest < UMBaseTest
@@ -271,7 +277,7 @@ class PeriodicallyTest < UMBaseTest
271
277
  rescue Cancel
272
278
  cancel = 1
273
279
  end
274
- 2.times { machine.snooze }
280
+ 5.times { machine.snooze }
275
281
  assert_equal 0, machine.pending_count
276
282
  t1 = monotonic_clock
277
283
  assert_in_range 0.05..0.08, t1 - t0
@@ -532,6 +538,30 @@ class WriteTest < UMBaseTest
532
538
  end
533
539
  end
534
540
 
541
+ class WriteAsyncTest < UMBaseTest
542
+ def test_write_async
543
+ r, w = IO.pipe
544
+
545
+ assert_equal 0, machine.pending_count
546
+ machine.write_async(w.fileno, 'foo')
547
+ assert_equal 1, machine.pending_count
548
+
549
+ machine.snooze
550
+ assert_equal 0, machine.pending_count
551
+ assert_equal 'foo', r.readpartial(3)
552
+ end
553
+
554
+ def test_write_async_bad_fd
555
+ r, _w = IO.pipe
556
+
557
+ assert_equal 0, machine.pending_count
558
+ machine.write_async(r.fileno, 'foo')
559
+ assert_equal 1, machine.pending_count
560
+ machine.snooze
561
+ assert_equal 0, machine.pending_count
562
+ end
563
+ end
564
+
535
565
  class CloseTest < UMBaseTest
536
566
  def test_close
537
567
  r, w = IO.pipe
@@ -545,25 +575,31 @@ class CloseTest < UMBaseTest
545
575
 
546
576
  assert_raises(Errno::EBADF) { machine.close(w.fileno) }
547
577
  end
548
- end
549
-
550
- class ShutdownTest < UMBaseTest
551
- def make_socket_pair
552
- port = 10000 + rand(30000)
553
- server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
554
- @machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
555
- @machine.bind(server_fd, '127.0.0.1', port)
556
- @machine.listen(server_fd, UM::SOMAXCONN)
557
578
 
558
- client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
559
- @machine.connect(client_conn_fd, '127.0.0.1', port)
579
+ def test_close_bad_fd
580
+ r, w = IO.pipe
581
+ machine.close(w.fileno)
582
+
583
+ assert_raises(Errno::EBADF) { machine.close(w.fileno) }
584
+ end
585
+ end
560
586
 
561
- server_conn_fd = @machine.accept(server_fd)
587
+ class CloseAsyncTest < UMBaseTest
588
+ def test_close_async
589
+ r, w = IO.pipe
590
+ machine.write(w.fileno, 'foo')
591
+ assert_equal 'foo', r.readpartial(3)
562
592
 
563
- @machine.close(server_fd)
564
- [client_conn_fd, server_conn_fd]
593
+ assert_equal 0, machine.pending_count
594
+ machine.close_async(w.fileno)
595
+ assert_equal 1, machine.pending_count
596
+ machine.snooze
597
+ assert_equal 0, machine.pending_count
598
+ assert_equal '', r.read
565
599
  end
600
+ end
566
601
 
602
+ class ShutdownTest < UMBaseTest
567
603
  def test_shutdown
568
604
  c_fd, s_fd = make_socket_pair
569
605
  res = @machine.send(c_fd, 'abc', 3, 0)
@@ -595,6 +631,37 @@ class ShutdownTest < UMBaseTest
595
631
  end
596
632
  end
597
633
 
634
+ class ShutdownAsyncTest < UMBaseTest
635
+ def test_shutdown_async
636
+ c_fd, s_fd = make_socket_pair
637
+ res = @machine.send(c_fd, 'abc', 3, 0)
638
+ assert_equal 3, res
639
+
640
+ buf = +''
641
+ res = @machine.recv(s_fd, buf, 256, 0)
642
+ assert_equal 3, res
643
+ assert_equal 'abc', buf
644
+
645
+ res = @machine.shutdown_async(c_fd, UM::SHUT_WR)
646
+ assert_equal c_fd, res
647
+
648
+ machine.sleep(0.01)
649
+ assert_raises(Errno::EPIPE) { @machine.send(c_fd, 'abc', 3, 0) }
650
+
651
+ res = @machine.shutdown_async(s_fd, UM::SHUT_RD)
652
+ assert_equal s_fd, res
653
+
654
+ res = @machine.recv(s_fd, buf, 256, 0)
655
+ assert_equal 0, res
656
+
657
+ res = @machine.shutdown_async(c_fd, UM::SHUT_RDWR)
658
+ assert_equal c_fd, res
659
+ ensure
660
+ @machine.close(c_fd)
661
+ @machine.close(s_fd)
662
+ end
663
+ end
664
+
598
665
  class AcceptTest < UMBaseTest
599
666
  def setup
600
667
  super
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uringmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: '0.14'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner