uringmachine 0.26.0 → 0.27.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba017ea6da0eb880e2366157a7f1ff6ea936d32816ed2be45e3b3ca96b3c7408
4
- data.tar.gz: 22f556023080078623fd8618122fe6707fa4cfdf6654b6c43e122f524c91c645
3
+ metadata.gz: 1211434f5b2f4aad1e47d1bd166204cce6792a80e3ef845ae4dee097d8e1ea45
4
+ data.tar.gz: eff370b4564c88c3daf57cbed3d8b165113ddee697ee1b16aa63ad3452a38c02
5
5
  SHA512:
6
- metadata.gz: c08b772dd22791c297fc38dab3e8a73453ace1b54ee5c9e7b31ef127598987efd37fdfebf009b9202d3b550674006caf380a0e20fb50e4c2a3bee7e302afa97b
7
- data.tar.gz: 1f35f727b26808a0bca4c24c19ee41ef4400766ec1b3b7033dbd39fea635168f93757825c4bf9e38d065c66652cd5120ce1880f8ef484233abdaa8bd9dff3b84
6
+ metadata.gz: cdb26cf58a41a9f7ad2d025ccc473b0088f4c3e622a65058b1ee6e0aeb95c0eb3775f980cccb60e81b2213ed8fb49263c6500b2c05fbbb71abb156be00dca636
7
+ data.tar.gz: 06f509928387d2812578c143dfb96ed031be10994c7b43908babfef02d72d149b8f2a3932dee7a4d3577150b5512addb8b7c44d5b7ea2e63dddb7f2b427df604
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.27.0 2026-02-15
2
+
3
+ - Add `.pr_set_child_subreaper` method
4
+ - Add `#send_fd`, `#recv_fd` methods
5
+
1
6
  # 0.26.0 2026-02-13
2
7
 
3
8
  - Reimplement um_op lifecycle tracking
data/TODO.md CHANGED
@@ -64,19 +64,6 @@ stream = UM::Stream.new(conn) # io mode
64
64
  - select on multiple queues (ala Go)
65
65
  - select on mixture of queues and fds
66
66
 
67
- (see also simplified op management below)
68
-
69
- ## simplified op management
70
-
71
- Op lifecycle management can be much much simpler
72
-
73
- - make all ops heap-allocated
74
- - clear up state transitions:
75
-
76
- - kernel-side state: unsubmitted, submitted, completed, done (for multishot ops)
77
- - app-side state: unsubmitted, submitted, ...
78
-
79
-
80
67
  ## ops
81
68
 
82
69
  - splice / - tee
@@ -104,7 +91,8 @@ We're still missing:
104
91
  - ability to supply buffer to `get_line` and `get_string`
105
92
  - allow read to eof, maybe with `read_to_eof`
106
93
 
107
- For the sake of performance, simplicity and explicitness, we change the API as follows:
94
+ For the sake of performance, simplicity and explicitness, we change the API as
95
+ follows:
108
96
 
109
97
  ```ruby
110
98
  stream.get_line(buf, limit)
data/ext/um/um.c CHANGED
@@ -977,6 +977,93 @@ VALUE um_shutdown_async(struct um *machine, int fd, int how) {
977
977
  return INT2NUM(fd);
978
978
  }
979
979
 
980
+ struct send_recv_fd_data {
981
+ struct msghdr msgh;
982
+ char iobuf[1];
983
+ struct iovec iov;
984
+ union { // Ancillary data buffer, wrapped in a union for alignment
985
+ char buf[CMSG_SPACE(sizeof(int))];
986
+ struct cmsghdr align;
987
+ } u;
988
+ };
989
+
990
+ inline void recv_fd_prepare(struct send_recv_fd_data *data) {
991
+ memset(&data->msgh, 0, sizeof(data->msgh));
992
+ data->iov.iov_base = data->iobuf;
993
+ data->iov.iov_len = sizeof(data->iobuf);
994
+ data->msgh.msg_iov = &data->iov;
995
+ data->msgh.msg_iovlen = 1;
996
+ data->msgh.msg_control = data->u.buf;
997
+ data->msgh.msg_controllen = sizeof(data->u.buf);
998
+ }
999
+
1000
+ inline void send_fd_prepare(struct send_recv_fd_data *data, int fd) {
1001
+ memset(&data->msgh, 0, sizeof(data->msgh));
1002
+ data->iov.iov_base = data->iobuf;
1003
+ data->iov.iov_len = sizeof(data->iobuf);
1004
+ data->msgh.msg_iov = &data->iov;
1005
+ data->msgh.msg_iovlen = 1;
1006
+ data->msgh.msg_control = data->u.buf;
1007
+ data->msgh.msg_controllen = sizeof(data->u.buf);
1008
+
1009
+ struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&data->msgh);
1010
+ cmsgp->cmsg_level = SOL_SOCKET;
1011
+ cmsgp->cmsg_type = SCM_RIGHTS;
1012
+ cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
1013
+ memcpy(CMSG_DATA(cmsgp), &fd, sizeof(fd));
1014
+ }
1015
+
1016
+ VALUE um_send_fd(struct um *machine, int sock_fd, int fd) {
1017
+ struct send_recv_fd_data data;
1018
+ send_fd_prepare(&data, fd);
1019
+
1020
+ VALUE ret = Qnil;
1021
+ struct um_op *op = um_op_acquire(machine);
1022
+ um_prep_op(machine, op, OP_SENDMSG, 2, 0);
1023
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
1024
+ io_uring_prep_sendmsg(sqe, sock_fd, &data.msgh, 0);
1025
+
1026
+ ret = um_yield(machine);
1027
+
1028
+ if (likely(um_verify_op_completion(machine, op, true))) ret = INT2NUM(fd);
1029
+ um_op_release(machine, op);
1030
+
1031
+ RAISE_IF_EXCEPTION(ret);
1032
+ RB_GC_GUARD(ret);
1033
+ return ret;
1034
+ }
1035
+
1036
+ inline int recv_fd_get_fd(struct send_recv_fd_data *data) {
1037
+ int fd;
1038
+ struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&data->msgh);
1039
+
1040
+ if (cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(int)) ||
1041
+ cmsgp->cmsg_level != SOL_SOCKET || cmsgp->cmsg_type != SCM_RIGHTS
1042
+ ) um_raise_on_error_result(-EINVAL);
1043
+
1044
+ memcpy(&fd, CMSG_DATA(cmsgp), sizeof(int));
1045
+ return fd;
1046
+ }
1047
+
1048
+ VALUE um_recv_fd(struct um *machine, int sock_fd) {
1049
+ struct send_recv_fd_data data;
1050
+ recv_fd_prepare(&data);
1051
+
1052
+ struct um_op *op = um_op_acquire(machine);
1053
+ um_prep_op(machine, op, OP_RECVMSG, 2, 0);
1054
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
1055
+ io_uring_prep_recvmsg(sqe, sock_fd, &data.msgh, 0);
1056
+
1057
+ VALUE ret = um_yield(machine);
1058
+
1059
+ if (likely(um_verify_op_completion(machine, op, true))) ret = INT2NUM(recv_fd_get_fd(&data));
1060
+ um_op_release(machine, op);
1061
+
1062
+ RAISE_IF_EXCEPTION(ret);
1063
+ RB_GC_GUARD(ret);
1064
+ return ret;
1065
+ }
1066
+
980
1067
  VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
981
1068
  struct um_op *op = um_op_acquire(machine);
982
1069
  um_prep_op(machine, op, OP_OPEN, 2, 0);
data/ext/um/um.h CHANGED
@@ -51,9 +51,11 @@ enum um_op_kind {
51
51
 
52
52
  OP_ACCEPT,
53
53
  OP_RECV,
54
+ OP_RECVMSG,
54
55
  OP_SEND,
55
- OP_SENDV,
56
56
  OP_SEND_BUNDLE,
57
+ OP_SENDMSG,
58
+ OP_SENDV,
57
59
  OP_SOCKET,
58
60
  OP_CONNECT,
59
61
  OP_BIND,
@@ -349,6 +351,8 @@ VALUE um_getsockopt(struct um *machine, int fd, int level, int opt);
349
351
  VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value);
350
352
  VALUE um_shutdown(struct um *machine, int fd, int how);
351
353
  VALUE um_shutdown_async(struct um *machine, int fd, int how);
354
+ VALUE um_send_fd(struct um *machine, int sock_fd, int fd);
355
+ VALUE um_recv_fd(struct um *machine, int sock_fd);
352
356
 
353
357
  void um_async_op_set(VALUE self, struct um *machine, struct um_op *op);
354
358
  VALUE um_async_op_await(struct um_async_op *async_op);
data/ext/um/um_class.c CHANGED
@@ -5,6 +5,8 @@
5
5
  #include <unistd.h>
6
6
  #include <sys/socket.h>
7
7
  #include <sys/inotify.h>
8
+ #include <linux/prctl.h>
9
+ #include <sys/prctl.h>
8
10
 
9
11
  VALUE cUM;
10
12
  VALUE eUMError;
@@ -328,6 +330,8 @@ VALUE UM_schedule(VALUE self, VALUE fiber, VALUE value) {
328
330
  /* Runs the given block, interrupting its execution if its runtime exceeds the
329
331
  * given timeout interval (in seconds).
330
332
  *
333
+ * - https://www.man7.org/linux/man-pages//man3/io_uring_prep_timeoute.3.html
334
+ *
331
335
  * @param interval [Number] timeout interval in seconds
332
336
  * @param exception_class [any] timeout exception class
333
337
  * @return [any] block's return value
@@ -340,6 +344,8 @@ VALUE UM_timeout(VALUE self, VALUE interval, VALUE exception_class) {
340
344
  /* Puts the current fiber to sleep for the given time duration (in seconds),
341
345
  * yielding control to the next fiber in the runqueue.
342
346
  *
347
+ * - https://www.man7.org/linux/man-pages//man3/io_uring_prep_timeoute.3.html
348
+ *
343
349
  * @param duration [Number] sleep duration in seconds
344
350
  * @return [void]
345
351
  */
@@ -349,6 +355,8 @@ VALUE UM_sleep(VALUE self, VALUE duration) {
349
355
  }
350
356
 
351
357
  /* Runs the given block at regular time intervals in an infinite loop.
358
+ *
359
+ * - https://www.man7.org/linux/man-pages//man3/io_uring_prep_timeoute.3.html
352
360
  *
353
361
  * @param interval [Number] time interval (in seconds) between consecutive invocations
354
362
  * @return [void]
@@ -359,21 +367,23 @@ VALUE UM_periodically(VALUE self, VALUE interval) {
359
367
  }
360
368
 
361
369
  /* call-seq:
362
- * machine.read(fd, buffer, maxlen[, buffer_offset[, file_offset]]) -> bytes_read
370
+ * machine.read(fd, buffer, maxlen, buffer_offset = nil, file_offset = nil) -> bytes_read
363
371
  *
364
372
  * Reads up to `maxlen` bytes from the given `fd` into the given buffer. The
365
373
  * optional `buffer_offset` parameter determines the position in the buffer into
366
374
  * which the data will be read. A negative `buffer_offset` denotes a position
367
375
  * relative to the end of the buffer, e.g. a value of `-1` means the data will
368
376
  * be appended to the buffer.
377
+ *
378
+ * - https://www.man7.org/linux/man-pages/man2/read.2.html
379
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_read.3.html
369
380
  *
370
- * @overload read(fd, buffer, maxlen, buffer_offset: nil, file_offset: nil)
371
- * @param fd [Integer] file descriptor
372
- * @param buffer [String, IO::Buffer] buffer
373
- * @param maxlen [Integer] maximum number of bytes to read
374
- * @param buffer_offset [Integer] optional buffer offset to read into
375
- * @param file_offset [Integer] optional file offset to read from
376
- * @return [Integer] number of bytes read
381
+ * @param fd [Integer] file descriptor
382
+ * @param buffer [String, IO::Buffer] buffer
383
+ * @param maxlen [Integer] maximum number of bytes to read
384
+ * @param buffer_offset [Integer] optional buffer offset to read into
385
+ * @param file_offset [Integer] optional file offset to read from
386
+ * @return [Integer] number of bytes read
377
387
  */
378
388
  VALUE UM_read(int argc, VALUE *argv, VALUE self) {
379
389
  struct um *machine = um_get_machine(self);
@@ -398,6 +408,8 @@ VALUE UM_read(int argc, VALUE *argv, VALUE self) {
398
408
  * buffer group should have been previously setup using `#setup_buffer_ring`.
399
409
  * Read data is yielded in an infinite loop to the given block.
400
410
  *
411
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_read_multishot.3.html
412
+ *
401
413
  * @param fd [Integer] file descriptor
402
414
  * @param bgid [Integer] buffer group id
403
415
  * @return [void]
@@ -408,17 +420,19 @@ VALUE UM_read_each(VALUE self, VALUE fd, VALUE bgid) {
408
420
  }
409
421
 
410
422
  /* call-seq:
411
- * machine.write(fd, buffer[, len[, file_offset]]) -> bytes_written
423
+ * machine.write(fd, buffer, len = nil, file_offset = nil) -> bytes_written
412
424
  *
413
425
  * Writes up to `len` bytes from the given buffer to the given `fd`. If `len`,
414
426
  * is not given, the entire buffer length is used.
415
427
  *
416
- * @overload write(fd, buffer, len: nil, file_offset: nil)
417
- * @param fd [Integer] file descriptor
418
- * @param buffer [String, IO::Buffer] buffer
419
- * @param len [Integer] maximum number of bytes to write
420
- * @param file_offset [Integer] optional file offset to write to
421
- * @return [Integer] number of bytes written
428
+ * - https://man7.org/linux/man-pages/man2/write.2.html
429
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_write.3.html
430
+ *
431
+ * @param fd [Integer] file descriptor
432
+ * @param buffer [String, IO::Buffer] buffer
433
+ * @param len [Integer] maximum number of bytes to write
434
+ * @param file_offset [Integer] optional file offset to write to
435
+ * @return [Integer] number of bytes written
422
436
  */
423
437
  VALUE UM_write(int argc, VALUE *argv, VALUE self) {
424
438
  struct um *machine = um_get_machine(self);
@@ -435,7 +449,7 @@ VALUE UM_write(int argc, VALUE *argv, VALUE self) {
435
449
  }
436
450
 
437
451
  /* call-seq:
438
- * machine.writev(fd, *buffers[, file_offset]) -> bytes_written
452
+ * machine.writev(fd, *buffers, file_offset = nil) -> bytes_written
439
453
  *
440
454
  * Writes from the given buffers into the given fd. This method does not
441
455
  * guarantee that all data will be written. The application code should check
@@ -443,11 +457,13 @@ VALUE UM_write(int argc, VALUE *argv, VALUE self) {
443
457
  * repeat the operation after adjusting the buffers accordingly. See also
444
458
  * `#sendv`.
445
459
  *
446
- * @overload writev(fd, *buffers, file_offset: nil)
447
- * @param fd [Integer] file descriptor
448
- * @param *buffers [Array<String, IO::Buffer>] data buffers
449
- * @param file_offset [Integer] optional file offset to write to
450
- * @return [Integer] number of bytes written
460
+ * - https://man7.org/linux/man-pages/man2/writev.2.html
461
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_writev.3.html
462
+ *
463
+ * @param fd [Integer] file descriptor
464
+ * @param *buffers [Array<String, IO::Buffer>] data buffers
465
+ * @param file_offset [Integer] optional file offset to write to
466
+ * @return [Integer] number of bytes written
451
467
  */
452
468
  VALUE UM_writev(int argc, VALUE *argv, VALUE self) {
453
469
  struct um *machine = um_get_machine(self);
@@ -460,7 +476,7 @@ VALUE UM_writev(int argc, VALUE *argv, VALUE self) {
460
476
  }
461
477
 
462
478
  /* call-seq:
463
- * machine.write_async(fd, buffer[, len[, file_offset]]) -> buffer
479
+ * machine.write_async(fd, buffer, len = nil, file_offset = nil) -> buffer
464
480
  *
465
481
  * Writes up to `len` bytes from the given buffer to the given `fd`. If `len`,
466
482
  * is not given, the entire buffer length is used. This method submits the
@@ -468,12 +484,14 @@ VALUE UM_writev(int argc, VALUE *argv, VALUE self) {
468
484
  * improve performance in situations where the application does not care about
469
485
  * whether the I/O operation succeeds or not.
470
486
  *
471
- * @overload write_async(fd, buffer, len: nil, file_offset: nil)
472
- * @param fd [Integer] file descriptor
473
- * @param buffer [String, IO::Buffer] buffer
474
- * @param len [Integer] maximum number of bytes to write
475
- * @param file_offset [Integer] optional file offset to write to
476
- * @return [String, IO::Buffer] buffer
487
+ * - https://man7.org/linux/man-pages/man2/write.2.html
488
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_write.3.html
489
+ *
490
+ * @param fd [Integer] file descriptor
491
+ * @param buffer [String, IO::Buffer] buffer
492
+ * @param len [Integer] maximum number of bytes to write
493
+ * @param file_offset [Integer] optional file offset to write to
494
+ * @return [String, IO::Buffer] buffer
477
495
  */
478
496
  VALUE UM_write_async(int argc, VALUE *argv, VALUE self) {
479
497
  struct um *machine = um_get_machine(self);
@@ -496,6 +514,9 @@ VALUE UM_write_async(int argc, VALUE *argv, VALUE self) {
496
514
  *
497
515
  * Returns information about a file.
498
516
  *
517
+ * - https://www.man7.org/linux/man-pages/man2/statx.2.html
518
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_statx.3.html
519
+ *
499
520
  * @param dirfd [Integer] file or directory descriptor
500
521
  * @param path [String, nil] file path
501
522
  * @param flags [Integer] flags
@@ -512,6 +533,9 @@ VALUE UM_statx(VALUE self, VALUE dirfd, VALUE path, VALUE flags, VALUE mask) {
512
533
  *
513
534
  * Closes the given file descriptor.
514
535
  *
536
+ * - https://www.man7.org/linux/man-pages/man2/close.2.html
537
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_close.3.html
538
+ *
515
539
  * @param fd [Integer] file descriptor
516
540
  * @return [0] success
517
541
  */
@@ -527,6 +551,9 @@ VALUE UM_close(VALUE self, VALUE fd) {
527
551
  * not wait for it to complete. This method may be used to improve performance
528
552
  * in cases where the application des not care whether it succeeds or not.
529
553
  *
554
+ * - https://www.man7.org/linux/man-pages/man2/close.2.html
555
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_close.3.html
556
+ *
530
557
  * @param fd [Integer] file descriptor
531
558
  * @return [Integer] file descriptor
532
559
  */
@@ -540,6 +567,9 @@ VALUE UM_close_async(VALUE self, VALUE fd) {
540
567
  *
541
568
  * Accepts an incoming TCP connection.
542
569
  *
570
+ * - https://www.man7.org/linux/man-pages/man2/accept4.2.html
571
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_accept.3.html
572
+ *
543
573
  * @param server_fd [Integer] listening socket file descriptor
544
574
  * @return [Integer] connection file descriptor
545
575
  */
@@ -554,6 +584,9 @@ VALUE UM_accept(VALUE self, VALUE server_fd) {
554
584
  * Repeatedly accepts incoming TCP connections in a loop, yielding the
555
585
  * connection file descriptors to the given block.
556
586
  *
587
+ * - https://www.man7.org/linux/man-pages/man2/accept4.2.html
588
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_accept.3.html
589
+ *
557
590
  * @param server_fd [Integer] listening socket file descriptor
558
591
  * @return [void]
559
592
  */
@@ -563,11 +596,14 @@ VALUE UM_accept_each(VALUE self, VALUE server_fd) {
563
596
  }
564
597
 
565
598
  /* call-seq:
566
- * machine.accept_each(server_fd, queue)
599
+ * machine.accept_into_queue(server_fd, queue)
567
600
  *
568
601
  * Repeatedly accepts incoming TCP connections in a loop, pushing the connection
569
602
  * file descriptors into the given queue.
570
603
  *
604
+ * - https://www.man7.org/linux/man-pages/man2/accept4.2.html
605
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_accept.3.html
606
+ *
571
607
  * @param server_fd [Integer] listening socket file descriptor
572
608
  * @param queue [UM::Queue] connection queue
573
609
  * @return [void]
@@ -582,6 +618,9 @@ VALUE UM_accept_into_queue(VALUE self, VALUE server_fd, VALUE queue) {
582
618
  *
583
619
  * Creates a socket.
584
620
  *
621
+ * - https://www.man7.org/linux/man-pages/man2/socket.2.html
622
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_socket.3.html
623
+ *
585
624
  * @param domain [Integer] socket domain
586
625
  * @param type [Integer] socket type
587
626
  * @param protocol [Integer] socket protocol
@@ -598,6 +637,9 @@ VALUE UM_socket(VALUE self, VALUE domain, VALUE type, VALUE protocol, VALUE flag
598
637
  *
599
638
  * Shuts down a socket for sending and/or receiving.
600
639
  *
640
+ * - https://man7.org/linux/man-pages/man2/shutdown.2.html
641
+ * - https://man7.org/linux/man-pages/man3/io_uring_prep_shutdown.3.html
642
+ *
601
643
  * @param fd [Integer] file descriptor
602
644
  * @param how [Integer] how the socket should be shutdown
603
645
  * @return [0] success
@@ -614,6 +656,9 @@ VALUE UM_shutdown(VALUE self, VALUE fd, VALUE how) {
614
656
  * improve performance in situations where the application does not care about
615
657
  * whether the operation succeeds or not.
616
658
  *
659
+ * - https://man7.org/linux/man-pages/man2/shutdown.2.html
660
+ * - https://man7.org/linux/man-pages/man3/io_uring_prep_shutdown.3.html
661
+ *
617
662
  * @param fd [Integer] file descriptor
618
663
  * @param how [Integer] how the socket should be shutdown
619
664
  * @return [0] success
@@ -623,11 +668,47 @@ VALUE UM_shutdown_async(VALUE self, VALUE fd, VALUE how) {
623
668
  return um_shutdown_async(machine, NUM2INT(fd), NUM2INT(how));
624
669
  }
625
670
 
671
+ /* call-seq:
672
+ * machine.send_fd(sock_fd, fd) -> fd
673
+ *
674
+ * Sends the given file descriptor over the given socket.
675
+ *
676
+ * - https://www.man7.org/linux/man-pages/man3/sendmsg.3p.html
677
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_sendmsg.3.html
678
+ *
679
+ * @param sock_fd [Integer] socket file descriptor
680
+ * @param fd [Integer] file descriptor to send
681
+ * @return [Integer] file descriptor to send
682
+ */
683
+ VALUE UM_send_fd(VALUE self, VALUE sock_fd, VALUE fd) {
684
+ struct um *machine = um_get_machine(self);
685
+ return um_send_fd(machine, NUM2INT(sock_fd), NUM2INT(fd));
686
+ }
687
+
688
+ /* call-seq:
689
+ * machine.recv_fd(sock_fd) -> fd
690
+ *
691
+ * Receives a file descriptor over the given socket.
692
+ *
693
+ * - https://www.man7.org/linux/man-pages/man3/recvmsg.3p.html
694
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_recvmsg.3.html
695
+ *
696
+ * @param sock_fd [Integer] socket file descriptor
697
+ * @return [Integer] rececived file descriptor
698
+ */
699
+ VALUE UM_recv_fd(VALUE self, VALUE sock_fd) {
700
+ struct um *machine = um_get_machine(self);
701
+ return um_recv_fd(machine, NUM2INT(sock_fd));
702
+ }
703
+
626
704
  /* call-seq:
627
705
  * machine.connect(fd, host, port) -> 0
628
706
  *
629
707
  * Connects the given socket to the given host and port.
630
708
  *
709
+ * - https://www.man7.org/linux/man-pages/man2/connect.2.html
710
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_connect.3.html
711
+ *
631
712
  * @param fd [Integer] file descriptor
632
713
  * @param host [String] hostname or IP address
633
714
  * @param port [Integer] port number
@@ -652,6 +733,9 @@ VALUE UM_connect(VALUE self, VALUE fd, VALUE host, VALUE port) {
652
733
  * the data in the buffer, unless `UM::MSG_WAITALL` is specified in the flags
653
734
  * mask.
654
735
  *
736
+ * - https://www.man7.org/linux/man-pages/man2/send.2.html
737
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_send.3.html
738
+ *
655
739
  * @param fd [Integer] file descriptor
656
740
  * @param buffer [String, IO::Buffer] buffer
657
741
  * @param len [Integer] number of bytes to send
@@ -671,6 +755,9 @@ VALUE UM_send(VALUE self, VALUE fd, VALUE buffer, VALUE len, VALUE flags) {
671
755
  * Sends data on the given socket from the given buffers. This method is only
672
756
  * available on Linux kernel >= 6.17. This method is guaranteed to send
673
757
  *
758
+ * - https://www.man7.org/linux/man-pages/man2/send.2.html
759
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_send.3.html
760
+ *
674
761
  * @overload sendv(fd, *buffers)
675
762
  * @param fd [Integer] file descriptor
676
763
  * @param *buffers [Array<String, IO::Buffer>] buffers
@@ -695,6 +782,9 @@ VALUE UM_sendv(int argc, VALUE *argv, VALUE self) {
695
782
  * buffer group. The buffer group should have been previously registered using
696
783
  * `#setup_buffer_ring`.
697
784
  *
785
+ * - https://www.man7.org/linux/man-pages/man2/send.2.html
786
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_send.3.html
787
+ *
698
788
  * @overload send_bundle(fd, bgid, *buffers)
699
789
  * @param fd [Integer] file descriptor
700
790
  * @param bgid [Integer] buffer group id
@@ -722,6 +812,9 @@ VALUE UM_send_bundle(int argc, VALUE *argv, VALUE self) {
722
812
  *
723
813
  * Receives data from the given socket.
724
814
  *
815
+ * - https://www.man7.org/linux/man-pages/man2/recv.2.html
816
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_recv.3.html
817
+ *
725
818
  * @param fd [Integer] file descriptor
726
819
  * @param buffer [String, IO::Buffer] buffer
727
820
  * @param maxlen [Integer] maximum number of bytes to receive
@@ -740,6 +833,9 @@ VALUE UM_recv(VALUE self, VALUE fd, VALUE buffer, VALUE maxlen, VALUE flags) {
740
833
  * given buffer group id. The buffer group should have been previously setup
741
834
  * using `#setup_buffer_ring`.
742
835
  *
836
+ * - https://www.man7.org/linux/man-pages/man2/recv.2.html
837
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_recv.3.html
838
+ *
743
839
  * @param fd [Integer] file descriptor
744
840
  * @param bgid [Integer] buffer group id
745
841
  * @param flags [Integer] flags mask
@@ -755,6 +851,9 @@ VALUE UM_recv_each(VALUE self, VALUE fd, VALUE bgid, VALUE flags) {
755
851
  *
756
852
  * Binds the given socket to the given host and port.
757
853
  *
854
+ * - https://www.man7.org/linux/man-pages/man2/bind.2.html
855
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_bind.3.html
856
+ *
758
857
  * @param fd [Integer] file descriptor
759
858
  * @param host [String] hostname or IP address
760
859
  * @param port [Integer] port number
@@ -783,6 +882,9 @@ VALUE UM_bind(VALUE self, VALUE fd, VALUE host, VALUE port) {
783
882
  *
784
883
  * Starts listening for incoming connections on the given socket.
785
884
  *
885
+ * - https://www.man7.org/linux/man-pages/man2/listen.2.html
886
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_listen.3.html
887
+ *
786
888
  * @param fd [Integer] file descriptor
787
889
  * @param backlog [String] pending connection queue length
788
890
  * @return [0] success
@@ -815,6 +917,9 @@ static inline int numeric_value(VALUE value) {
815
917
  *
816
918
  * Returns the value of a socket option.
817
919
  *
920
+ * - https://www.man7.org/linux/man-pages//man2/getsockopt.2.html
921
+ * - https://www.man7.org/linux/man-pages//man3/io_uring_prep_cmd.3.html
922
+ *
818
923
  * @param fd [Integer] file descriptor
819
924
  * @param level [Integer] level
820
925
  * @param opt [Integer] level
@@ -826,10 +931,13 @@ VALUE UM_getsockopt(VALUE self, VALUE fd, VALUE level, VALUE opt) {
826
931
  }
827
932
 
828
933
  /* call-seq:
829
- * machine.getsockopt(fd, level, opt, value) -> 0
934
+ * machine.setsockopt(fd, level, opt, value) -> 0
830
935
  *
831
936
  * Sets the value of a socket option.
832
937
  *
938
+ * - https://www.man7.org/linux/man-pages//man2/setsockopt.2.html
939
+ * - https://www.man7.org/linux/man-pages//man3/io_uring_prep_cmd.3.html
940
+ *
833
941
  * @param fd [Integer] file descriptor
834
942
  * @param level [Integer] level
835
943
  * @param opt [Integer] level
@@ -847,6 +955,9 @@ VALUE UM_setsockopt(VALUE self, VALUE fd, VALUE level, VALUE opt, VALUE value) {
847
955
  * Synchronizes access to the given mutex. The mutex is locked, the given block
848
956
  * is executed and finally the mutex is unlocked.
849
957
  *
958
+ * - https://www.man7.org/linux/man-pages/man2/futex.2.html
959
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_futex_wait.3.html
960
+ *
850
961
  * @param mutex [UM::Mutex] mutex
851
962
  * @return [any] block return value
852
963
  */
@@ -861,6 +972,9 @@ VALUE UM_mutex_synchronize(VALUE self, VALUE mutex) {
861
972
  *
862
973
  * Pushes a value to the tail of the given queue.
863
974
  *
975
+ * - https://www.man7.org/linux/man-pages/man2/futex.2.html
976
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_futex_wait.3.html
977
+ *
864
978
  * @param queue [UM::Queue] queue
865
979
  * @param value [any] value
866
980
  * @return [UM::Queue] queue
@@ -876,6 +990,9 @@ VALUE UM_queue_push(VALUE self, VALUE queue, VALUE value) {
876
990
  *
877
991
  * removes a value from the tail of the given queue.
878
992
  *
993
+ * - https://www.man7.org/linux/man-pages/man2/futex.2.html
994
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_futex_wait.3.html
995
+ *
879
996
  * @param queue [UM::Queue] queue
880
997
  * @return [any] value
881
998
  */
@@ -890,6 +1007,9 @@ VALUE UM_queue_pop(VALUE self, VALUE queue) {
890
1007
  *
891
1008
  * Pushes a value to the head of the given queue.
892
1009
  *
1010
+ * - https://www.man7.org/linux/man-pages/man2/futex.2.html
1011
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_futex_wait.3.html
1012
+ *
893
1013
  * @param queue [UM::Queue] queue
894
1014
  * @param value [any] value
895
1015
  * @return [UM::Queue] queue
@@ -905,6 +1025,9 @@ VALUE UM_queue_unshift(VALUE self, VALUE queue, VALUE value) {
905
1025
  *
906
1026
  * removes a value from the head of the given queue.
907
1027
  *
1028
+ * - https://www.man7.org/linux/man-pages/man2/futex.2.html
1029
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_futex_wait.3.html
1030
+ *
908
1031
  * @param queue [UM::Queue] queue
909
1032
  * @return [any] value
910
1033
  */
@@ -933,6 +1056,9 @@ VALUE UM_open_complete(VALUE arg) {
933
1056
  * file descriptor is passed to the block, and the file is automatically closed
934
1057
  * when the block returns.
935
1058
  *
1059
+ * - https://www.man7.org/linux/man-pages/man2/open.2.html
1060
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_openat.3.html
1061
+ *
936
1062
  * @param pathname [String] file path
937
1063
  * @param flags [Integer] flags mask
938
1064
  * @return [Integer] fd
@@ -955,6 +1081,9 @@ VALUE UM_open(VALUE self, VALUE pathname, VALUE flags) {
955
1081
  * Waits for readiness of the given file descriptor according to the given event
956
1082
  * mask.
957
1083
  *
1084
+ * - https://www.man7.org/linux/man-pages/man2/poll.2.html
1085
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_poll_add.3.html
1086
+ *
958
1087
  * @param fd [Integer] file descriptor
959
1088
  * @param mask [Integer] events mask
960
1089
  * @return [Integer] fd
@@ -970,6 +1099,9 @@ VALUE UM_poll(VALUE self, VALUE fd, VALUE mask) {
970
1099
  * Waits for readyness of at least one fd for read, write or exception condition
971
1100
  * from the given fds. This method provides a similar interface to `IO#select`.
972
1101
  *
1102
+ * - https://www.man7.org/linux/man-pages/man2/select.2.html
1103
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_poll_add.3.html
1104
+ *
973
1105
  * @param read_fds [Array<Integer>] file descriptors for reading
974
1106
  * @param write_fds [Array<Integer>] file descriptors for writing
975
1107
  * @param except_fds [Array<Integer>] file descriptors for exception
@@ -986,6 +1118,9 @@ VALUE UM_select(VALUE self, VALUE read_fds, VALUE write_fds, VALUE except_fds) {
986
1118
  * Waits for a process to change state. The process to wait for can be specified
987
1119
  * as a pid or as a pidfd, according to the given `idtype` and `id`.
988
1120
  *
1121
+ * - https://www.man7.org/linux/man-pages/man2/waitid.2.html
1122
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_waitid.3.html
1123
+ *
989
1124
  * @param idtype [Integer] id type
990
1125
  * @param id [Integer] id
991
1126
  * @param options [Integer] options
@@ -1077,6 +1212,8 @@ VALUE UM_ssl_write(VALUE self, VALUE ssl, VALUE buf, VALUE len) {
1077
1212
  *
1078
1213
  * Creates a pipe, returning the file descriptors for the read and write ends.
1079
1214
  *
1215
+ * - https://www.man7.org/linux/man-pages/man2/pipe.2.html
1216
+ *
1080
1217
  * @return [Array<Integer>] array containing the read and write file descriptors
1081
1218
  */
1082
1219
  VALUE UM_pipe(VALUE self) {
@@ -1095,6 +1232,8 @@ VALUE UM_pipe(VALUE self) {
1095
1232
  *
1096
1233
  * Creates a pair of connected sockets, returning the two file descriptors.
1097
1234
  *
1235
+ * - https://www.man7.org/linux/man-pages/man2/socketpair.2.html
1236
+ *
1098
1237
  * @param domain [Integer] domain
1099
1238
  * @param type [Integer] type
1100
1239
  * @param protocol [Integer] protocol
@@ -1117,6 +1256,8 @@ VALUE UM_socketpair(VALUE self, VALUE domain, VALUE type, VALUE protocol) {
1117
1256
  * Creates a file descriptor representing the given process pid. The file
1118
1257
  * descriptor can then be used with methods such as `#waitid` or `.pidfd_open`.
1119
1258
  *
1259
+ * - https://www.man7.org/linux/man-pages/man2/pidfd_open.2.html
1260
+ *
1120
1261
  * @param pid [Integer] process pid
1121
1262
  * @return [Integer] file descriptor
1122
1263
  */
@@ -1135,6 +1276,8 @@ VALUE UM_pidfd_open(VALUE self, VALUE pid) {
1135
1276
  *
1136
1277
  * Sends a signal to a pidfd.
1137
1278
  *
1279
+ * - https://www.man7.org/linux/man-pages/man2/pidfd_send_signal.2.html
1280
+ *
1138
1281
  * @param fd [Integer] pidfd
1139
1282
  * @param sig [Integer] signal
1140
1283
  * @return [Integer] pidfd
@@ -1151,38 +1294,6 @@ VALUE UM_pidfd_send_signal(VALUE self, VALUE fd, VALUE sig) {
1151
1294
  return fd;
1152
1295
  }
1153
1296
 
1154
- /* :nodoc:
1155
- */
1156
- VALUE UM_io_nonblock_p(VALUE self, VALUE io) {
1157
- int fd = rb_io_descriptor(io);
1158
- int oflags = fcntl(fd, F_GETFL);
1159
- if (oflags == -1) return Qnil;
1160
-
1161
- return (oflags & O_NONBLOCK) ? Qtrue : Qfalse;
1162
- }
1163
-
1164
- /* :nodoc:
1165
- */
1166
- VALUE UM_io_set_nonblock(VALUE self, VALUE io, VALUE nonblock) {
1167
- int fd = rb_io_descriptor(io);
1168
- int oflags = fcntl(fd, F_GETFL);
1169
- if (oflags == -1) return Qnil;
1170
-
1171
- if (RTEST(nonblock)) {
1172
- if (!(oflags & O_NONBLOCK)) {
1173
- oflags |= O_NONBLOCK;
1174
- fcntl(fd, F_SETFL, oflags);
1175
- }
1176
- }
1177
- else {
1178
- if (oflags & O_NONBLOCK) {
1179
- oflags &= ~O_NONBLOCK;
1180
- fcntl(fd, F_SETFL, oflags);
1181
- }
1182
- }
1183
- return nonblock;
1184
- }
1185
-
1186
1297
  /* call-seq:
1187
1298
  * UringMachine.kernel_version -> version
1188
1299
  *
@@ -1212,6 +1323,8 @@ VALUE UM_debug(VALUE self, VALUE str) {
1212
1323
  *
1213
1324
  * Creates an inotify file descriptor.
1214
1325
  *
1326
+ * - https://www.man7.org/linux/man-pages/man2/inotify_init.2.html
1327
+ *
1215
1328
  * @return [Integer] file descriptor
1216
1329
  */
1217
1330
  VALUE UM_inotify_init(VALUE self) {
@@ -1228,6 +1341,8 @@ VALUE UM_inotify_init(VALUE self) {
1228
1341
  *
1229
1342
  * Adds a watch on the given inotify file descriptor.
1230
1343
  *
1344
+ * - https://www.man7.org/linux/man-pages/man2/inotify_add_watch.2.html
1345
+ *
1231
1346
  * @param fd [Integer] inotify file descriptor
1232
1347
  * @param path [String] file/directory path
1233
1348
  * @param mask [Integer] inotify event mask
@@ -1269,6 +1384,8 @@ static inline VALUE inotify_get_events(char *buf, size_t len) {
1269
1384
  * descriptor. Each event is returned as a hash containing the watch descriptor,
1270
1385
  * the file name, and the event mask.
1271
1386
  *
1387
+ * - https://www.man7.org/linux/man-pages/man7/inotify.7.html
1388
+ *
1272
1389
  * @param fd [Integer] inotify file descriptor
1273
1390
  * @return [Array<Hash>] array of one or more events
1274
1391
  */
@@ -1281,12 +1398,46 @@ VALUE UM_inotify_get_events(VALUE self, VALUE fd) {
1281
1398
  return inotify_get_events(buf, ret);
1282
1399
  }
1283
1400
 
1401
+ /* call-seq:
1402
+ * UringMachine.pr_set_child_subreaper(vaule) -> value
1403
+ *
1404
+ * Sets/unsets the "child subreaper" attribute of the calling process.
1405
+ *
1406
+ * - https://man7.org/linux/man-pages/man2/PR_SET_CHILD_SUBREAPER.2const.html
1407
+ *
1408
+ * @param value [bool] set/unset value
1409
+ * @return [bool] set/unset value
1410
+ */
1411
+ VALUE UM_pr_set_child_subreaper(VALUE self, VALUE set) {
1412
+ int ret = prctl(PR_SET_CHILD_SUBREAPER, RTEST(set) ? 1 : 0);
1413
+ if (ret) {
1414
+ int e = errno;
1415
+ rb_syserr_fail(e, strerror(e));
1416
+ }
1417
+
1418
+ return set;
1419
+ }
1420
+
1284
1421
  void Init_UM(void) {
1285
1422
  rb_ext_ractor_safe(true);
1286
1423
 
1287
1424
  cUM = rb_define_class("UringMachine", rb_cObject);
1288
1425
  rb_define_alloc_func(cUM, UM_allocate);
1289
1426
 
1427
+ rb_define_singleton_method(cUM, "pipe", UM_pipe, 0);
1428
+ rb_define_singleton_method(cUM, "socketpair", UM_socketpair, 3);
1429
+ rb_define_singleton_method(cUM, "pidfd_open", UM_pidfd_open, 1);
1430
+ rb_define_singleton_method(cUM, "pidfd_send_signal", UM_pidfd_send_signal, 2);
1431
+
1432
+ rb_define_singleton_method(cUM, "kernel_version", UM_kernel_version, 0);
1433
+ rb_define_singleton_method(cUM, "debug", UM_debug, 1);
1434
+
1435
+ rb_define_singleton_method(cUM, "inotify_init", UM_inotify_init, 0);
1436
+ rb_define_singleton_method(cUM, "inotify_add_watch", UM_inotify_add_watch, 3);
1437
+
1438
+ rb_define_singleton_method(cUM, "pr_set_child_subreaper", UM_pr_set_child_subreaper, 1);
1439
+
1440
+
1290
1441
  rb_define_method(cUM, "initialize", UM_initialize, -1);
1291
1442
  rb_define_method(cUM, "size", UM_size, 0);
1292
1443
  rb_define_method(cUM, "mark", UM_mark_m, 1);
@@ -1302,19 +1453,6 @@ void Init_UM(void) {
1302
1453
 
1303
1454
  rb_define_method(cUM, "setup_buffer_ring", UM_setup_buffer_ring, 2);
1304
1455
 
1305
- rb_define_singleton_method(cUM, "pipe", UM_pipe, 0);
1306
- rb_define_singleton_method(cUM, "socketpair", UM_socketpair, 3);
1307
- rb_define_singleton_method(cUM, "pidfd_open", UM_pidfd_open, 1);
1308
- rb_define_singleton_method(cUM, "pidfd_send_signal", UM_pidfd_send_signal, 2);
1309
-
1310
- rb_define_singleton_method(cUM, "io_nonblock?", UM_io_nonblock_p, 1);
1311
- rb_define_singleton_method(cUM, "io_set_nonblock", UM_io_set_nonblock, 2);
1312
- rb_define_singleton_method(cUM, "kernel_version", UM_kernel_version, 0);
1313
- rb_define_singleton_method(cUM, "debug", UM_debug, 1);
1314
-
1315
- rb_define_singleton_method(cUM, "inotify_init", UM_inotify_init, 0);
1316
- rb_define_singleton_method(cUM, "inotify_add_watch", UM_inotify_add_watch, 3);
1317
-
1318
1456
  rb_define_method(cUM, "schedule", UM_schedule, 2);
1319
1457
  rb_define_method(cUM, "snooze", UM_snooze, 0);
1320
1458
  rb_define_method(cUM, "timeout", UM_timeout, 2);
@@ -1364,6 +1502,8 @@ void Init_UM(void) {
1364
1502
  rb_define_method(cUM, "socket", UM_socket, 4);
1365
1503
  rb_define_method(cUM, "shutdown", UM_shutdown, 2);
1366
1504
  rb_define_method(cUM, "shutdown_async", UM_shutdown_async, 2);
1505
+ rb_define_method(cUM, "send_fd", UM_send_fd, 2);
1506
+ rb_define_method(cUM, "recv_fd", UM_recv_fd, 1);
1367
1507
 
1368
1508
  rb_define_method(cUM, "prep_timeout", UM_prep_timeout, 1);
1369
1509
 
data/ext/um/um_op.c CHANGED
@@ -18,9 +18,11 @@ const char * um_op_kind_name(enum um_op_kind kind) {
18
18
  case OP_STATX: return "OP_STATX";
19
19
  case OP_ACCEPT: return "OP_ACCEPT";
20
20
  case OP_RECV: return "OP_RECV";
21
+ case OP_RECVMSG: return "OP_RECVMSG";
21
22
  case OP_SEND: return "OP_SEND";
22
- case OP_SENDV: return "OP_SENDV";
23
23
  case OP_SEND_BUNDLE: return "OP_SEND_BUNDLE";
24
+ case OP_SENDMSG: return "OP_SENDMSG";
25
+ case OP_SENDV: return "OP_SENDV";
24
26
  case OP_SOCKET: return "OP_SOCKET";
25
27
  case OP_CONNECT: return "OP_CONNECT";
26
28
  case OP_BIND: return "OP_BIND";
data/grant-2025/tasks.md CHANGED
@@ -129,10 +129,10 @@
129
129
 
130
130
  - [v] Postgres test
131
131
 
132
- - [ ] Ruby Fiber::Scheduler interface
132
+ - [v] Ruby Fiber::Scheduler interface
133
133
  - [v] Make a PR for resetting the scheduler and resetting the fiber non-blocking flag.
134
134
  - [v] hook for close
135
- - [ ] hooks for send/recv/sendmsg/recvmsg
135
+ - [x] hooks for send/recv/sendmsg/recvmsg (Ruby core PR pending for send/recv)
136
136
 
137
137
  - [v] SSL
138
138
  - [v] setup custom BIO
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.26.0'
4
+ VERSION = '0.27.0'
5
5
  end
data/test/test_um.rb CHANGED
@@ -733,7 +733,7 @@ class ReadEachTest < UMBaseTest
733
733
  class TOError < StandardError; end
734
734
 
735
735
  def test_read_each_timeout
736
- r, w = IO.pipe
736
+ r, _w = IO.pipe
737
737
  bgid = machine.setup_buffer_ring(4096, 1024)
738
738
 
739
739
  bufs = []
@@ -1344,7 +1344,7 @@ class AcceptEachTest < UMBaseTest
1344
1344
  def test_accept_each_closed
1345
1345
  count = 0
1346
1346
  done = nil
1347
- f = @machine.spin do
1347
+ @machine.spin do
1348
1348
  machine.accept_each(@server.fileno) do |fd|
1349
1349
  count += 1
1350
1350
  end
@@ -1816,7 +1816,7 @@ class RecvEachTest < UMBaseTest
1816
1816
 
1817
1817
  def test_recv_each_timeout
1818
1818
  t = Thread.new do
1819
- conn = @server.accept
1819
+ @server.accept
1820
1820
  sleep
1821
1821
  end
1822
1822
 
@@ -1847,7 +1847,7 @@ class RecvEachTest < UMBaseTest
1847
1847
 
1848
1848
  def test_recv_each_shutdown
1849
1849
  t = Thread.new do
1850
- conn = @server.accept
1850
+ @server.accept
1851
1851
  sleep
1852
1852
  end
1853
1853
 
@@ -1861,7 +1861,7 @@ class RecvEachTest < UMBaseTest
1861
1861
  bufs = []
1862
1862
  e = nil
1863
1863
 
1864
- f = machine.spin {
1864
+ machine.spin {
1865
1865
  machine.sleep(0.01)
1866
1866
  machine.shutdown(fd, UM::SHUT_RDWR)
1867
1867
  }
@@ -1881,6 +1881,90 @@ class RecvEachTest < UMBaseTest
1881
1881
  end
1882
1882
  end
1883
1883
 
1884
+ class SendRecvFdTest < UMBaseTest
1885
+ def setup
1886
+ @s1_fd, @s2_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
1887
+ super
1888
+ end
1889
+
1890
+ def teardown
1891
+ [@s1_fd, @s2_fd].each { machine.close(it) rescue nil }
1892
+ super
1893
+ end
1894
+
1895
+ class TOError < StandardError; end
1896
+
1897
+ def test_send_recv_fd
1898
+ r_fd, w_fd = UM.pipe
1899
+
1900
+ res = machine.send_fd(@s1_fd, w_fd)
1901
+ assert_equal w_fd, res
1902
+
1903
+ fd = machine.recv_fd(@s2_fd)
1904
+ assert_kind_of Integer, fd
1905
+ refute_equal w_fd, fd
1906
+
1907
+ machine.close(w_fd)
1908
+ machine.write(fd, 'foobar')
1909
+ machine.close(fd)
1910
+
1911
+ buf = +''
1912
+ res = machine.read(r_fd, buf, 12)
1913
+ assert_equal 6, res
1914
+ assert_equal 'foobar', buf
1915
+ end
1916
+
1917
+ def test_send_recv_fd_fork
1918
+ pid = fork do
1919
+ m = UM.new
1920
+ fd = m.recv_fd(@s2_fd)
1921
+ m.write(fd, 'Hello!')
1922
+ ensure
1923
+ m.close(fd) rescue nil
1924
+ end
1925
+
1926
+ r_fd, w_fd = UM.pipe
1927
+
1928
+ res = machine.send_fd(@s1_fd, w_fd)
1929
+ assert_equal w_fd, res
1930
+ Process.wait(pid)
1931
+ pid = nil
1932
+ machine.close(w_fd)
1933
+
1934
+ buf = +''
1935
+ len = machine.timeout(1, TOError) { machine.read(r_fd, buf, 12) }
1936
+ assert_equal 6, len
1937
+ assert_equal 'Hello!', buf
1938
+ ensure
1939
+ if pid
1940
+ Process.kill('KILL', pid) rescue nil
1941
+ Process.wait(pid) rescue nil
1942
+ end
1943
+ machine.close(r_fd) rescue nil
1944
+ machine.close(w_fd) rescue nil
1945
+ end
1946
+
1947
+ def test_send_fd_bad_sock_fd
1948
+ _r_fd, w_fd = UM.pipe
1949
+ assert_raises(Errno::ENOTSOCK) { machine.send_fd(0, w_fd) }
1950
+ end
1951
+
1952
+ def test_send_fd_bad_fd
1953
+ assert_raises(TypeError) { machine.send_fd(@s1_fd, nil) }
1954
+ assert_raises(Errno::EBADF) { machine.send_fd(@s1_fd, 1111) }
1955
+ end
1956
+
1957
+ def test_recv_fd_bad_msg
1958
+ buf = "\0" * 1000
1959
+ machine.write(@s1_fd, buf)
1960
+
1961
+ assert_raises(Errno::EINVAL) {
1962
+ res = machine.recv_fd(@s2_fd)
1963
+ p res: res
1964
+ }
1965
+ end
1966
+ end
1967
+
1884
1968
  class BindTest < UMBaseTest
1885
1969
  def setup
1886
1970
  super
@@ -2836,23 +2920,6 @@ class SendBundleTest < UMBaseTest
2836
2920
  end
2837
2921
  end
2838
2922
 
2839
- class NonBlockTest < UMBaseTest
2840
- def test_io_nonblock?
2841
- assert_equal false, UM.io_nonblock?(STDIN)
2842
- end
2843
-
2844
- def test_io_set_nonblock
2845
- r, _w = IO.pipe
2846
- assert_equal true, UM.io_nonblock?(r)
2847
-
2848
- UM.io_set_nonblock(r, false)
2849
- assert_equal false, UM.io_nonblock?(r)
2850
-
2851
- UM.io_set_nonblock(r, true)
2852
- assert_equal true, UM.io_nonblock?(r)
2853
- end
2854
- end
2855
-
2856
2923
  class MetricsTest < UMBaseTest
2857
2924
  def test_metrics_empty
2858
2925
  assert_equal({
@@ -3159,3 +3226,39 @@ class FileWatchTest < UMBaseTest
3159
3226
  machine.join(f)
3160
3227
  end
3161
3228
  end
3229
+
3230
+ class SetChildSubreaperTest < Minitest::Test
3231
+ def test_pr_set_child_subreaper
3232
+ r, w = IO.pipe
3233
+ UM.pr_set_child_subreaper(true)
3234
+
3235
+ child_pid = fork {
3236
+ r2, w2 = IO.pipe
3237
+ pid = fork {
3238
+ r.close
3239
+ w.close
3240
+ w2.close
3241
+ r2.read
3242
+ r2.close
3243
+ sleep(0.01)
3244
+ }
3245
+ w << pid
3246
+ w.close
3247
+ r2.close
3248
+ w2 << 'done'
3249
+ w2.close
3250
+ }
3251
+ Process.wait(child_pid)
3252
+
3253
+ w.close
3254
+ msg = r.read
3255
+ r.close
3256
+
3257
+ refute msg.empty?
3258
+ grand_child_pid = msg.to_i
3259
+ refute_equal 0, grand_child_pid
3260
+
3261
+ res = Process.wait(grand_child_pid)
3262
+ assert_equal grand_child_pid, res
3263
+ end
3264
+ end
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.26.0
4
+ version: 0.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner