uringmachine 0.19 → 0.20.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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/TODO.md +40 -0
  4. data/examples/bm_fileno.rb +33 -0
  5. data/examples/bm_mutex.rb +85 -0
  6. data/examples/bm_mutex_single.rb +33 -0
  7. data/examples/bm_queue.rb +27 -28
  8. data/examples/bm_send.rb +2 -5
  9. data/examples/bm_snooze.rb +20 -42
  10. data/examples/fiber_scheduler_demo.rb +15 -51
  11. data/examples/fiber_scheduler_fork.rb +24 -0
  12. data/examples/nc_ssl.rb +71 -0
  13. data/ext/um/extconf.rb +5 -15
  14. data/ext/um/um.c +73 -42
  15. data/ext/um/um.h +21 -11
  16. data/ext/um/um_async_op_class.c +2 -2
  17. data/ext/um/um_buffer.c +1 -1
  18. data/ext/um/um_class.c +94 -23
  19. data/ext/um/um_const.c +51 -3
  20. data/ext/um/um_mutex_class.c +1 -1
  21. data/ext/um/um_queue_class.c +1 -1
  22. data/ext/um/um_stream.c +5 -5
  23. data/ext/um/um_stream_class.c +3 -0
  24. data/ext/um/um_sync.c +22 -27
  25. data/ext/um/um_utils.c +59 -19
  26. data/grant-2025/journal.md +229 -0
  27. data/grant-2025/tasks.md +66 -0
  28. data/lib/uringmachine/fiber_scheduler.rb +180 -48
  29. data/lib/uringmachine/version.rb +1 -1
  30. data/lib/uringmachine.rb +6 -0
  31. data/test/test_fiber_scheduler.rb +138 -0
  32. data/test/test_stream.rb +2 -2
  33. data/test/test_um.rb +451 -33
  34. data/vendor/liburing/.github/workflows/ci.yml +94 -1
  35. data/vendor/liburing/.github/workflows/test_build.c +9 -0
  36. data/vendor/liburing/configure +27 -0
  37. data/vendor/liburing/examples/Makefile +6 -0
  38. data/vendor/liburing/examples/helpers.c +8 -0
  39. data/vendor/liburing/examples/helpers.h +5 -0
  40. data/vendor/liburing/liburing.spec +1 -1
  41. data/vendor/liburing/src/Makefile +9 -3
  42. data/vendor/liburing/src/include/liburing/barrier.h +11 -5
  43. data/vendor/liburing/src/include/liburing/io_uring/query.h +41 -0
  44. data/vendor/liburing/src/include/liburing/io_uring.h +50 -0
  45. data/vendor/liburing/src/include/liburing/sanitize.h +16 -4
  46. data/vendor/liburing/src/include/liburing.h +445 -121
  47. data/vendor/liburing/src/liburing-ffi.map +15 -0
  48. data/vendor/liburing/src/liburing.map +8 -0
  49. data/vendor/liburing/src/sanitize.c +4 -1
  50. data/vendor/liburing/src/setup.c +7 -4
  51. data/vendor/liburing/test/232c93d07b74.c +4 -16
  52. data/vendor/liburing/test/Makefile +15 -1
  53. data/vendor/liburing/test/accept.c +2 -13
  54. data/vendor/liburing/test/conn-unreach.c +132 -0
  55. data/vendor/liburing/test/fd-pass.c +32 -7
  56. data/vendor/liburing/test/fdinfo.c +39 -12
  57. data/vendor/liburing/test/fifo-futex-poll.c +114 -0
  58. data/vendor/liburing/test/fifo-nonblock-read.c +1 -12
  59. data/vendor/liburing/test/futex.c +1 -1
  60. data/vendor/liburing/test/helpers.c +99 -2
  61. data/vendor/liburing/test/helpers.h +9 -0
  62. data/vendor/liburing/test/io_uring_passthrough.c +6 -12
  63. data/vendor/liburing/test/mock_file.c +379 -0
  64. data/vendor/liburing/test/mock_file.h +47 -0
  65. data/vendor/liburing/test/nop.c +2 -2
  66. data/vendor/liburing/test/nop32-overflow.c +150 -0
  67. data/vendor/liburing/test/nop32.c +126 -0
  68. data/vendor/liburing/test/pipe.c +166 -0
  69. data/vendor/liburing/test/poll-race-mshot.c +13 -1
  70. data/vendor/liburing/test/recv-mshot-fair.c +81 -34
  71. data/vendor/liburing/test/recvsend_bundle.c +1 -1
  72. data/vendor/liburing/test/resize-rings.c +2 -0
  73. data/vendor/liburing/test/ring-query.c +322 -0
  74. data/vendor/liburing/test/ringbuf-loop.c +87 -0
  75. data/vendor/liburing/test/runtests.sh +2 -2
  76. data/vendor/liburing/test/send-zerocopy.c +43 -5
  77. data/vendor/liburing/test/send_recv.c +102 -32
  78. data/vendor/liburing/test/shutdown.c +2 -12
  79. data/vendor/liburing/test/socket-nb.c +3 -14
  80. data/vendor/liburing/test/socket-rw-eagain.c +2 -12
  81. data/vendor/liburing/test/socket-rw-offset.c +2 -12
  82. data/vendor/liburing/test/socket-rw.c +2 -12
  83. data/vendor/liburing/test/sqe-mixed-bad-wrap.c +87 -0
  84. data/vendor/liburing/test/sqe-mixed-nop.c +82 -0
  85. data/vendor/liburing/test/sqe-mixed-uring_cmd.c +153 -0
  86. data/vendor/liburing/test/timestamp.c +56 -19
  87. data/vendor/liburing/test/vec-regbuf.c +2 -4
  88. data/vendor/liburing/test/wq-aff.c +7 -0
  89. metadata +24 -2
data/test/test_um.rb CHANGED
@@ -142,6 +142,31 @@ class ScheduleTest < UMBaseTest
142
142
  assert_kind_of TOError, e
143
143
  end
144
144
 
145
+ def test_timeout_stress
146
+ skip
147
+ # GC.stress = true
148
+ c = 0
149
+ fs = 100.times.map {
150
+ machine.spin {
151
+ q = UM::Queue.new
152
+ 1000.times {
153
+ machine.sleep rand(0.001..0.005)
154
+ begin
155
+ machine.timeout(rand(0.001..0.06), TOError) do
156
+ machine.shift(q)
157
+ end
158
+ rescue => _e
159
+ c += 1
160
+ STDOUT << '*' if c % 1000 == 0
161
+ end
162
+ }
163
+ }
164
+ }
165
+ machine.join(*fs)
166
+ ensure
167
+ GC.stress = false
168
+ end
169
+
145
170
  def test_timeout_with_raising_block
146
171
  e = nil
147
172
  begin
@@ -302,7 +327,7 @@ class PeriodicallyTest < UMBaseTest
302
327
  rescue Cancel
303
328
  cancel = 1
304
329
  end
305
- 5.times { machine.snooze }
330
+ 20.times { machine.snooze }
306
331
  assert_equal 0, machine.pending_count
307
332
  t1 = monotonic_clock
308
333
  assert_in_range 0.05..0.08, t1 - t0
@@ -394,6 +419,51 @@ class ReadTest < UMBaseTest
394
419
  assert_equal 3, result
395
420
  assert_equal 'baz', sio.read
396
421
  end
422
+
423
+ def test_read_io_buffer
424
+ r, w = UM.pipe
425
+ machine.write(w, 'foobar')
426
+
427
+ read_buffer = IO::Buffer.new(3)
428
+ res = machine.read(r, read_buffer, 3)
429
+ assert_equal 3, res
430
+ assert_equal 'foo', read_buffer.get_string(0, 3)
431
+
432
+ machine.close(w)
433
+
434
+ res = machine.read(r, read_buffer)
435
+ assert_equal 3, res
436
+ assert_equal 'bar', read_buffer.get_string(0, 3)
437
+ end
438
+
439
+ def test_read_io_buffer_resize
440
+ r, w = UM.pipe
441
+ machine.write(w, 'foobar')
442
+ machine.close(w)
443
+
444
+ read_buffer = IO::Buffer.new(3)
445
+ res = machine.read(r, read_buffer, 6)
446
+ assert_equal 6, res
447
+ assert_equal 6, read_buffer.size
448
+ assert_equal 'foobar', read_buffer.get_string(0, res)
449
+
450
+ r, w = UM.pipe
451
+ machine.write(w, 'foobar')
452
+ machine.close(w)
453
+
454
+ read_buffer = IO::Buffer.new(3)
455
+ res = machine.read(r, read_buffer, 128, -1)
456
+ assert_equal 6, res
457
+ assert_equal 131, read_buffer.size
458
+ assert_equal 'foobar', read_buffer.get_string(3, res)
459
+ end
460
+
461
+ def test_read_invalid_buffer
462
+ r, _w = UM.pipe
463
+ assert_raises(UM::Error) {
464
+ machine.read(r, [])
465
+ }
466
+ end
397
467
  end
398
468
 
399
469
  class ReadEachTest < UMBaseTest
@@ -561,6 +631,54 @@ class WriteTest < UMBaseTest
561
631
  end
562
632
  assert_equal 0, machine.pending_count
563
633
  end
634
+
635
+ def test_write_io_buffer
636
+ r, w = UM.pipe
637
+
638
+ msg = 'Hello world'
639
+ write_buffer = IO::Buffer.new(msg.bytesize)
640
+ write_buffer.set_string(msg, 0)
641
+
642
+ machine.write(w, write_buffer)
643
+ machine.close(w)
644
+
645
+ str = +''
646
+ machine.read(r, str, 8192)
647
+ assert_equal msg, str
648
+ end
649
+
650
+ def test_write_io_buffer_with_len
651
+ r, w = UM.pipe
652
+ msg = 'Hello world'
653
+ write_buffer = IO::Buffer.new(msg.bytesize)
654
+ write_buffer.set_string(msg)
655
+
656
+ machine.write(w, write_buffer, 5)
657
+ machine.close(w)
658
+
659
+ str = +''
660
+ machine.read(r, str, 8192)
661
+ assert_equal 'Hello', str
662
+
663
+ r, w = UM.pipe
664
+ msg = 'Hello world'
665
+ write_buffer = IO::Buffer.new(msg.bytesize)
666
+ write_buffer.set_string(msg)
667
+
668
+ machine.write(w, write_buffer, -1)
669
+ machine.close(w)
670
+
671
+ str = +''
672
+ machine.read(r, str, 8192)
673
+ assert_equal 'Hello world', str
674
+ end
675
+
676
+ def test_write_invalid_buffer
677
+ _r, w = UM.pipe
678
+ assert_raises(UM::Error) {
679
+ machine.write(w, [])
680
+ }
681
+ end
564
682
  end
565
683
 
566
684
  class WriteAsyncTest < UMBaseTest
@@ -601,6 +719,28 @@ class WriteAsyncTest < UMBaseTest
601
719
  machine.snooze
602
720
  assert_equal 0, machine.pending_count
603
721
  end
722
+
723
+ def test_write_async_io_buffer
724
+ r, w = UM.pipe
725
+
726
+ msg = 'Hello world'
727
+ write_buffer = IO::Buffer.new(msg.bytesize)
728
+ write_buffer.set_string(msg)
729
+
730
+ machine.write_async(w, write_buffer)
731
+ 3.times { machine.snooze }
732
+ machine.close(w)
733
+
734
+ str = +''
735
+ machine.read(r, str, 8192)
736
+ assert_equal msg, str
737
+ end
738
+
739
+ def test_write_async_invalid_buffer
740
+ _r, w = UM.pipe
741
+
742
+ assert_raises(UM::Error) { machine.write_async(w, []) }
743
+ end
604
744
  end
605
745
 
606
746
  class CloseTest < UMBaseTest
@@ -618,7 +758,7 @@ class CloseTest < UMBaseTest
618
758
  end
619
759
 
620
760
  def test_close_bad_fd
621
- r, w = IO.pipe
761
+ _r, w = IO.pipe
622
762
  machine.close(w.fileno)
623
763
 
624
764
  assert_raises(Errno::EBADF) { machine.close(w.fileno) }
@@ -877,6 +1017,78 @@ class SendTest < UMBaseTest
877
1017
  ensure
878
1018
  t&.kill
879
1019
  end
1020
+
1021
+
1022
+ def test_send_io_buffer
1023
+ @port = assign_port
1024
+ @server = TCPServer.open('127.0.0.1', @port)
1025
+
1026
+ t = Thread.new do
1027
+ conn = @server.accept
1028
+ str = conn.readpartial(42)
1029
+ conn.write("You said: #{str} (#{str.bytesize})")
1030
+ sleep
1031
+ end
1032
+
1033
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1034
+ res = machine.connect(fd, '127.0.0.1', @port)
1035
+ assert_equal 0, res
1036
+
1037
+ buffer = IO::Buffer.new(6)
1038
+ buffer.set_string('foobar')
1039
+ res = machine.send(fd, buffer, 6, 0)
1040
+ assert_equal 6, res
1041
+
1042
+ buf = +''
1043
+ res = machine.read(fd, buf, 42)
1044
+ assert_equal 20, res
1045
+ assert_equal 'You said: foobar (6)', buf
1046
+ ensure
1047
+ t&.kill
1048
+ @server&.close
1049
+ end
1050
+
1051
+ def test_send_io_buffer_negative_len
1052
+ t = Thread.new do
1053
+ conn = @server.accept
1054
+ str = conn.readpartial(42)
1055
+ conn.write("You said: #{str} (#{str.bytesize})")
1056
+ sleep
1057
+ end
1058
+
1059
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1060
+ res = machine.connect(fd, '127.0.0.1', @port)
1061
+ assert_equal 0, res
1062
+
1063
+ buffer = IO::Buffer.new(6)
1064
+ buffer.set_string('foobar')
1065
+ res = machine.send(fd, buffer, -1, 0)
1066
+ assert_equal 6, res
1067
+
1068
+ buf = +''
1069
+ res = machine.read(fd, buf, 42)
1070
+ assert_equal 20, res
1071
+ assert_equal 'You said: foobar (6)', buf
1072
+ ensure
1073
+ t&.kill
1074
+ end
1075
+
1076
+ def test_send_invalid_buffer
1077
+ t = Thread.new do
1078
+ conn = @server.accept
1079
+ str = conn.readpartial(42)
1080
+ conn.write("You said: #{str} (#{str.bytesize})")
1081
+ sleep
1082
+ end
1083
+
1084
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1085
+ res = machine.connect(fd, '127.0.0.1', @port)
1086
+ assert_equal 0, res
1087
+
1088
+ assert_raises(UM::Error) { machine.send(fd, [], -1, 0) }
1089
+ ensure
1090
+ t&.kill
1091
+ end
880
1092
  end
881
1093
 
882
1094
  class RecvTest < UMBaseTest
@@ -909,6 +1121,25 @@ class RecvTest < UMBaseTest
909
1121
  ensure
910
1122
  t&.kill
911
1123
  end
1124
+
1125
+ def test_recv_io_buffer
1126
+ t = Thread.new do
1127
+ conn = @server.accept
1128
+ conn.write('foobar')
1129
+ sleep
1130
+ end
1131
+
1132
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1133
+ res = machine.connect(fd, '127.0.0.1', @port)
1134
+ assert_equal 0, res
1135
+
1136
+ buf = IO::Buffer.new(12)
1137
+ res = machine.recv(fd, buf, 12, 0)
1138
+ assert_equal 6, res
1139
+ assert_equal 'foobar', buf.get_string(0, 6)
1140
+ ensure
1141
+ t&.kill
1142
+ end
912
1143
  end
913
1144
 
914
1145
  class RecvEachTest < UMBaseTest
@@ -1094,6 +1325,25 @@ class SynchronizeTest < UMBaseTest
1094
1325
  assert_equal [11, 12, 13, 21, 22, 23], buf
1095
1326
  assert_equal 0, machine.pending_count
1096
1327
  end
1328
+
1329
+ def test_synchronize_multi
1330
+ mutex = UM::Mutex.new
1331
+ buf = []
1332
+ fibers = (1..8).map { |i|
1333
+ machine.spin do
1334
+ machine.synchronize(mutex) do
1335
+ buf << (i * 10) + 1
1336
+ machine.sleep(0.01)
1337
+ buf << (i * 10) + 2
1338
+ end
1339
+ machine.snooze
1340
+ buf << (i * 10) + 3
1341
+ end
1342
+ }
1343
+
1344
+ machine.join(*fibers)
1345
+ assert_equal [11, 12, 21, 13, 22, 31, 23, 32, 41, 33, 42, 51, 43, 52, 61, 53, 62, 71, 63, 72, 81, 73, 82, 83], buf
1346
+ end
1097
1347
  end
1098
1348
 
1099
1349
  class QueueTest < UMBaseTest
@@ -1310,12 +1560,67 @@ class PipeTest < UMBaseTest
1310
1560
  end
1311
1561
  end
1312
1562
 
1563
+ class PidfdTest < UMBaseTest
1564
+ def test_pidfd_open
1565
+ pid = fork { exit 13 }
1566
+ fd = UM.pidfd_open(pid)
1567
+ assert_kind_of Integer, fd
1568
+
1569
+ pid2, status = machine.waitid(P_PIDFD, fd, UM::WEXITED)
1570
+ assert_equal pid, pid2
1571
+ assert_equal 13, status
1572
+ ensure
1573
+ machine.close(fd) rescue nil
1574
+ Process.wait(pid) rescue nil
1575
+ end
1576
+
1577
+ def test_pidfd_open_invalid_pid
1578
+ assert_raises(SystemCallError) { UM.pidfd_open(Process.pid + 1) }
1579
+ end
1580
+
1581
+ def test_pidfd_send_signal
1582
+ fd = UM.pidfd_open(Process.pid)
1583
+ buf = []
1584
+ trap('SIGUSR1') { buf << :SIGUSR1 }
1585
+
1586
+ ret = UM.pidfd_send_signal(fd, UM::SIGUSR1)
1587
+ assert_equal fd, ret
1588
+ sleep 0.01
1589
+
1590
+ assert_equal [:SIGUSR1], buf
1591
+ ensure
1592
+ machine.close(fd) rescue nil
1593
+ end
1594
+
1595
+ def test_pidfd_send_signal_test
1596
+ fd = UM.pidfd_open(Process.pid)
1597
+ buf = []
1598
+ trap('SIGUSR1') { buf << :SIGUSR1 }
1599
+
1600
+ ret = UM.pidfd_send_signal(fd, 0)
1601
+ assert_equal fd, ret
1602
+ sleep 0.01
1603
+ assert_equal [], buf
1604
+
1605
+ pid = fork { exit 13 }
1606
+ fd2 = UM.pidfd_open(pid)
1607
+ assert_kind_of Integer, fd2
1608
+
1609
+ Process.wait(pid)
1610
+ assert_raises(SystemCallError) { UM.pidfd_send_signal(fd2, 0) }
1611
+ ensure
1612
+ machine.close(fd) rescue nil
1613
+ machine.close(fd2) rescue nil
1614
+ Process.wait(pid) rescue nil
1615
+ end
1616
+ end
1617
+
1313
1618
  class PollTest < UMBaseTest
1314
1619
  def test_poll
1315
1620
  rfd, wfd = UM.pipe
1316
1621
 
1317
1622
  events = []
1318
- f1 = machine.spin do
1623
+ machine.spin do
1319
1624
  events << :pre
1320
1625
  events << machine.poll(rfd, UM::POLLIN)
1321
1626
  events << :post
@@ -1341,38 +1646,81 @@ class PollTest < UMBaseTest
1341
1646
  end
1342
1647
  end
1343
1648
 
1344
- class WaitTest < UMBaseTest
1345
- def test_waitpid
1346
- skip if UM.kernel_version < 607
1347
-
1649
+ class WaitidTest < UMBaseTest
1650
+ def test_waitid
1348
1651
  msg = 'hello from child'
1652
+ _rfd, wfd = UM.pipe
1653
+ child_pid = fork do
1654
+ m = UM.new
1655
+ m.write(wfd, msg)
1656
+ m.close(wfd)
1657
+ exit 42
1658
+ end
1349
1659
 
1350
- rfd, wfd = UM.pipe
1351
- pid = fork do
1660
+ pid, status = machine.waitid(UM::P_PID, child_pid, UM::WEXITED)
1661
+ assert_equal child_pid, pid
1662
+ assert_equal 42, status
1663
+ ensure
1664
+ Process.wait(child_pid) rescue nil
1665
+ end
1666
+
1667
+ def test_waitid_invalid_pid
1668
+ assert_raises(Errno::ECHILD) {
1669
+ machine.waitid(UM::P_PID, Process.pid + 1, UM::WEXITED)
1670
+ }
1671
+ end
1672
+
1673
+ def test_waitid_invalid_idtype
1674
+ assert_raises(Errno::EINVAL) {
1675
+ machine.waitid(1234, 0, UM::WEXITED)
1676
+ }
1677
+ end
1678
+
1679
+ def test_waitid_invalid_options
1680
+ assert_raises(Errno::EINVAL) {
1681
+ machine.waitid(P_ALL, 0, 1234)
1682
+ }
1683
+ end
1684
+
1685
+ def test_waitid_P_ALL
1686
+ msg = 'hello from child'
1687
+ _rfd, wfd = UM.pipe
1688
+ child_pid = fork do
1352
1689
  m = UM.new
1353
1690
  m.write(wfd, msg)
1354
1691
  m.close(wfd)
1355
1692
  exit 42
1356
1693
  end
1357
1694
 
1358
- ret = machine.waitpid(pid, UM::WEXITED)
1359
- assert_kind_of Array, ret
1360
- assert_equal [pid, 42], ret
1695
+ pid, status = machine.waitid(UM::P_ALL, 0, UM::WEXITED)
1696
+ assert_equal child_pid, pid
1697
+ assert_equal 42, status
1698
+ ensure
1699
+ Process.wait(child_pid) rescue nil
1700
+ end
1361
1701
 
1362
- buf = +''
1363
- ret = machine.read(rfd, buf, 8192)
1364
- assert_equal msg.bytesize, ret
1365
- assert_equal msg, buf
1702
+ def test_waitid_P_PGID
1703
+ msg = 'hello from child'
1704
+ _rfd, wfd = UM.pipe
1705
+ child_pid = fork do
1706
+ m = UM.new
1707
+ m.write(wfd, msg)
1708
+ m.close(wfd)
1709
+ exit 42
1710
+ end
1711
+
1712
+ pid, status = machine.waitid(UM::P_PGID, Process.getpgrp, UM::WEXITED)
1713
+ assert_equal child_pid, pid
1714
+ assert_equal 42, status
1366
1715
  ensure
1367
- Process.wait(pid) rescue Errno::ECHILD
1716
+ Process.wait(child_pid) rescue nil
1368
1717
  end
1369
1718
 
1370
- def test_waitpid_any
1371
- skip if UM.kernel_version < 607
1719
+ def test_waitid_status
1720
+ skip if !machine.respond_to?(:waitid_status)
1372
1721
 
1373
1722
  msg = 'hello from child'
1374
-
1375
- rfd, wfd = UM.pipe
1723
+ _rfd, wfd = UM.pipe
1376
1724
  pid = fork do
1377
1725
  m = UM.new
1378
1726
  m.write(wfd, msg)
@@ -1380,23 +1728,76 @@ class WaitTest < UMBaseTest
1380
1728
  exit 42
1381
1729
  end
1382
1730
 
1383
- ret = machine.waitpid(0, UM::WEXITED)
1384
- assert_kind_of Array, ret
1385
- assert_equal [pid, 42], ret
1731
+ status = machine.waitid_status(UM::P_PID, pid, UM::WEXITED)
1732
+ assert_kind_of Process::Status, status
1733
+ assert_equal pid, status.pid
1734
+ assert_equal 42, status.exitstatus
1735
+ ensure
1736
+ Process.wait(pid) rescue nil
1737
+ end
1386
1738
 
1387
- buf = +''
1388
- ret = machine.read(rfd, buf, 8192)
1389
- assert_equal msg.bytesize, ret
1390
- assert_equal msg, buf
1739
+ def test_waitid_status_invalid_pid
1740
+ skip if !machine.respond_to?(:waitid_status)
1741
+
1742
+ assert_raises(Errno::ECHILD) {
1743
+ machine.waitid_status(UM::P_PID, Process.pid + 1, UM::WEXITED)
1744
+ }
1745
+ end
1746
+
1747
+ def test_waitid_status_invalid_idtype
1748
+ skip if !machine.respond_to?(:waitid_status)
1391
1749
 
1750
+ assert_raises(Errno::EINVAL) {
1751
+ machine.waitid_status(1234, 0, UM::WEXITED)
1752
+ }
1753
+ end
1754
+
1755
+ def test_waitid_status_invalid_options
1756
+ skip if !machine.respond_to?(:waitid_status)
1757
+
1758
+ assert_raises(Errno::EINVAL) {
1759
+ machine.waitid_status(P_ALL, 0, 1234)
1760
+ }
1761
+ end
1762
+
1763
+ def test_waitid_status_P_ALL
1764
+ skip if !machine.respond_to?(:waitid_status)
1765
+
1766
+ msg = 'hello from child'
1767
+ _rfd, wfd = UM.pipe
1768
+ pid = fork do
1769
+ m = UM.new
1770
+ m.write(wfd, msg)
1771
+ m.close(wfd)
1772
+ exit 42
1773
+ end
1774
+
1775
+ status = machine.waitid_status(UM::P_ALL, 0, UM::WEXITED)
1776
+ assert_kind_of Process::Status, status
1777
+ assert_equal pid, status.pid
1778
+ assert_equal 42, status.exitstatus
1392
1779
  ensure
1393
- Process.wait(pid) rescue Errno::ECHILD
1780
+ Process.wait(pid) rescue nil
1394
1781
  end
1395
1782
 
1396
- def test_waitpid_bad_pid
1397
- skip if UM.kernel_version < 607
1783
+ def test_waitid_status_P_PGID
1784
+ skip if !machine.respond_to?(:waitid_status)
1785
+
1786
+ msg = 'hello from child'
1787
+ _rfd, wfd = UM.pipe
1788
+ pid = fork do
1789
+ m = UM.new
1790
+ m.write(wfd, msg)
1791
+ m.close(wfd)
1792
+ exit 42
1793
+ end
1398
1794
 
1399
- assert_raises(Errno::ECHILD) { machine.waitpid(1, UM::WEXITED) }
1795
+ status = machine.waitid_status(UM::P_PGID, Process.getpgrp, UM::WEXITED)
1796
+ assert_kind_of Process::Status, status
1797
+ assert_equal pid, status.pid
1798
+ assert_equal 42, status.exitstatus
1799
+ ensure
1800
+ Process.wait(pid) rescue nil
1400
1801
  end
1401
1802
  end
1402
1803
 
@@ -1484,7 +1885,7 @@ class ForkTest < UMBaseTest
1484
1885
  assert_equal 3, ret
1485
1886
  assert_equal 'foo', buf
1486
1887
  ensure
1487
- Process.wait(child_pid) rescue Errno::ECHILD
1888
+ Process.wait(child_pid) rescue nil
1488
1889
  end
1489
1890
  end
1490
1891
 
@@ -1548,3 +1949,20 @@ class SendBundleTest < UMBaseTest
1548
1949
  assert_equal strs.map(&:to_s).join, buf
1549
1950
  end
1550
1951
  end
1952
+
1953
+ class NonBlockTest < UMBaseTest
1954
+ def test_io_nonblock?
1955
+ assert_equal false, UM.io_nonblock?(STDIN)
1956
+ end
1957
+
1958
+ def test_io_set_nonblock
1959
+ r, _w = IO.pipe
1960
+ assert_equal true, UM.io_nonblock?(r)
1961
+
1962
+ UM.io_set_nonblock(r, false)
1963
+ assert_equal false, UM.io_nonblock?(r)
1964
+
1965
+ UM.io_set_nonblock(r, true)
1966
+ assert_equal true, UM.io_nonblock?(r)
1967
+ end
1968
+ end