uringmachine 0.19.1 → 0.21.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -4
  3. data/CHANGELOG.md +32 -1
  4. data/TODO.md +0 -39
  5. data/examples/bm_fileno.rb +33 -0
  6. data/examples/bm_mutex.rb +85 -0
  7. data/examples/bm_mutex_single.rb +33 -0
  8. data/examples/bm_queue.rb +29 -29
  9. data/examples/bm_send.rb +2 -5
  10. data/examples/bm_snooze.rb +20 -42
  11. data/examples/bm_write.rb +4 -1
  12. data/examples/fiber_scheduler_demo.rb +15 -51
  13. data/examples/fiber_scheduler_fork.rb +24 -0
  14. data/examples/nc_ssl.rb +71 -0
  15. data/ext/um/extconf.rb +5 -15
  16. data/ext/um/um.c +310 -74
  17. data/ext/um/um.h +66 -29
  18. data/ext/um/um_async_op.c +1 -1
  19. data/ext/um/um_async_op_class.c +2 -2
  20. data/ext/um/um_buffer.c +1 -1
  21. data/ext/um/um_class.c +178 -31
  22. data/ext/um/um_const.c +51 -3
  23. data/ext/um/um_mutex_class.c +1 -1
  24. data/ext/um/um_op.c +37 -0
  25. data/ext/um/um_queue_class.c +1 -1
  26. data/ext/um/um_stream.c +5 -5
  27. data/ext/um/um_stream_class.c +3 -0
  28. data/ext/um/um_sync.c +28 -39
  29. data/ext/um/um_utils.c +59 -19
  30. data/grant-2025/journal.md +353 -0
  31. data/grant-2025/tasks.md +135 -0
  32. data/lib/uringmachine/fiber_scheduler.rb +316 -57
  33. data/lib/uringmachine/version.rb +1 -1
  34. data/lib/uringmachine.rb +6 -0
  35. data/test/test_fiber_scheduler.rb +640 -0
  36. data/test/test_stream.rb +2 -2
  37. data/test/test_um.rb +722 -54
  38. data/uringmachine.gemspec +5 -5
  39. data/vendor/liburing/.github/workflows/ci.yml +94 -1
  40. data/vendor/liburing/.github/workflows/test_build.c +9 -0
  41. data/vendor/liburing/configure +27 -0
  42. data/vendor/liburing/examples/Makefile +6 -0
  43. data/vendor/liburing/examples/helpers.c +8 -0
  44. data/vendor/liburing/examples/helpers.h +5 -0
  45. data/vendor/liburing/liburing.spec +1 -1
  46. data/vendor/liburing/src/Makefile +9 -3
  47. data/vendor/liburing/src/include/liburing/barrier.h +11 -5
  48. data/vendor/liburing/src/include/liburing/io_uring/query.h +41 -0
  49. data/vendor/liburing/src/include/liburing/io_uring.h +51 -0
  50. data/vendor/liburing/src/include/liburing/sanitize.h +16 -4
  51. data/vendor/liburing/src/include/liburing.h +458 -121
  52. data/vendor/liburing/src/liburing-ffi.map +16 -0
  53. data/vendor/liburing/src/liburing.map +8 -0
  54. data/vendor/liburing/src/sanitize.c +4 -1
  55. data/vendor/liburing/src/setup.c +7 -4
  56. data/vendor/liburing/test/232c93d07b74.c +4 -16
  57. data/vendor/liburing/test/Makefile +15 -1
  58. data/vendor/liburing/test/accept.c +2 -13
  59. data/vendor/liburing/test/bind-listen.c +175 -13
  60. data/vendor/liburing/test/conn-unreach.c +132 -0
  61. data/vendor/liburing/test/fd-pass.c +32 -7
  62. data/vendor/liburing/test/fdinfo.c +39 -12
  63. data/vendor/liburing/test/fifo-futex-poll.c +114 -0
  64. data/vendor/liburing/test/fifo-nonblock-read.c +1 -12
  65. data/vendor/liburing/test/futex.c +1 -1
  66. data/vendor/liburing/test/helpers.c +99 -2
  67. data/vendor/liburing/test/helpers.h +9 -0
  68. data/vendor/liburing/test/io_uring_passthrough.c +6 -12
  69. data/vendor/liburing/test/mock_file.c +379 -0
  70. data/vendor/liburing/test/mock_file.h +47 -0
  71. data/vendor/liburing/test/nop.c +2 -2
  72. data/vendor/liburing/test/nop32-overflow.c +150 -0
  73. data/vendor/liburing/test/nop32.c +126 -0
  74. data/vendor/liburing/test/pipe.c +166 -0
  75. data/vendor/liburing/test/poll-race-mshot.c +13 -1
  76. data/vendor/liburing/test/read-write.c +4 -4
  77. data/vendor/liburing/test/recv-mshot-fair.c +81 -34
  78. data/vendor/liburing/test/recvsend_bundle.c +1 -1
  79. data/vendor/liburing/test/resize-rings.c +2 -0
  80. data/vendor/liburing/test/ring-query.c +322 -0
  81. data/vendor/liburing/test/ringbuf-loop.c +87 -0
  82. data/vendor/liburing/test/ringbuf-read.c +4 -4
  83. data/vendor/liburing/test/runtests.sh +2 -2
  84. data/vendor/liburing/test/send-zerocopy.c +43 -5
  85. data/vendor/liburing/test/send_recv.c +103 -32
  86. data/vendor/liburing/test/shutdown.c +2 -12
  87. data/vendor/liburing/test/socket-nb.c +3 -14
  88. data/vendor/liburing/test/socket-rw-eagain.c +2 -12
  89. data/vendor/liburing/test/socket-rw-offset.c +2 -12
  90. data/vendor/liburing/test/socket-rw.c +2 -12
  91. data/vendor/liburing/test/sqe-mixed-bad-wrap.c +87 -0
  92. data/vendor/liburing/test/sqe-mixed-nop.c +82 -0
  93. data/vendor/liburing/test/sqe-mixed-uring_cmd.c +153 -0
  94. data/vendor/liburing/test/timestamp.c +56 -19
  95. data/vendor/liburing/test/vec-regbuf.c +2 -4
  96. data/vendor/liburing/test/wq-aff.c +7 -0
  97. metadata +37 -15
data/test/test_um.rb CHANGED
@@ -11,6 +11,80 @@ class UringMachineTest < Minitest::Test
11
11
  end
12
12
  end
13
13
 
14
+ class EntriesTest < Minitest::Test
15
+ def test_default_entries
16
+ m = UM.new
17
+ assert_equal 4096, m.entries
18
+ end
19
+
20
+ def test_custom_entries_value
21
+ m = UM.new(13)
22
+ assert_equal 13, m.entries
23
+ end
24
+ end
25
+
26
+ class SQPOLLTest < Minitest::Test
27
+ def test_sqpoll_timeout
28
+ m = UM.new(10, 0.05)
29
+
30
+ r, w = UM.pipe
31
+
32
+ buf = nil
33
+ t = Thread.new { buf = IO.new(r).readpartial(3) }
34
+
35
+ # let SQPOLL worker thread timeout
36
+ sleep(0.06)
37
+
38
+ # SQE is prepared but not submitted
39
+ m.write_async(w, 'foo')
40
+
41
+ # thread should timeout
42
+ ret = t.join(0.03)
43
+ assert_nil buf
44
+ ensure
45
+ t.kill rescue nil
46
+ end
47
+
48
+ def test_sqpoll_timeout_with_submit
49
+ m = UM.new(10, 0.05)
50
+
51
+ r, w = UM.pipe
52
+
53
+ buf = nil
54
+ t = Thread.new { buf = IO.new(r).readpartial(3) }
55
+
56
+ # let SQPOLL worker thread timeout
57
+ sleep(0.06)
58
+
59
+ # SQE is prepared but not submitted
60
+ m.write_async(w, 'foo')
61
+ ret = m.submit
62
+ assert_equal 1, ret
63
+
64
+ # thread should timeout
65
+ ret = t.join(0.1)
66
+ assert_equal 'foo', buf
67
+ ensure
68
+ t.kill rescue nil
69
+ end
70
+ end
71
+
72
+ class SubmitTest < UMBaseTest
73
+ def test_submit
74
+ _r, w = UM.pipe
75
+
76
+ machine.write_async(w, 'foo')
77
+ machine.write_async(w, 'bar')
78
+ machine.write_async(w, 'baz')
79
+
80
+ assert_equal 3, machine.pending_count
81
+ assert_equal 3, machine.submit
82
+ assert_equal 0, machine.submit
83
+ machine.snooze
84
+ assert_equal 0, machine.pending_count
85
+ end
86
+ end
87
+
14
88
  class SpinTest < UMBaseTest
15
89
  def test_spin
16
90
  x = nil
@@ -164,7 +238,7 @@ class ScheduleTest < UMBaseTest
164
238
  }
165
239
  machine.join(*fs)
166
240
  ensure
167
- GC.stress = false
241
+ GC.stress = false
168
242
  end
169
243
 
170
244
  def test_timeout_with_raising_block
@@ -327,16 +401,36 @@ class PeriodicallyTest < UMBaseTest
327
401
  rescue Cancel
328
402
  cancel = 1
329
403
  end
330
- 5.times { machine.snooze }
331
- assert_equal 0, machine.pending_count
404
+ assert_equal 1, cancel
332
405
  t1 = monotonic_clock
333
406
  assert_in_range 0.05..0.08, t1 - t0
334
407
  assert_in_range 4..6, count
335
- assert_equal 1, cancel
336
408
 
337
409
  end
338
410
  end
339
411
 
412
+ class StatsTest < UMBaseTest
413
+ def test_op_counts
414
+ _r, w = IO.pipe
415
+
416
+ assert_equal 0, machine.pending_count
417
+ assert_equal 0, machine.total_op_count
418
+ machine.write_async(w.fileno, 'foo')
419
+ assert_equal 1, machine.pending_count
420
+ assert_equal 1, machine.total_op_count
421
+ machine.snooze
422
+ assert_equal 0, machine.pending_count
423
+ assert_equal 1, machine.total_op_count
424
+
425
+ machine.write_async(w.fileno, 'foo')
426
+ assert_equal 1, machine.pending_count
427
+ assert_equal 2, machine.total_op_count
428
+ machine.snooze
429
+ assert_equal 0, machine.pending_count
430
+ assert_equal 2, machine.total_op_count
431
+ end
432
+ end
433
+
340
434
  class ReadTest < UMBaseTest
341
435
  def test_read
342
436
  r, w = IO.pipe
@@ -419,6 +513,64 @@ class ReadTest < UMBaseTest
419
513
  assert_equal 3, result
420
514
  assert_equal 'baz', sio.read
421
515
  end
516
+
517
+ def test_read_io_buffer
518
+ r, w = UM.pipe
519
+ machine.write(w, 'foobar')
520
+
521
+ read_buffer = IO::Buffer.new(3)
522
+ res = machine.read(r, read_buffer, 3)
523
+ assert_equal 3, res
524
+ assert_equal 'foo', read_buffer.get_string(0, 3)
525
+
526
+ machine.close(w)
527
+
528
+ res = machine.read(r, read_buffer)
529
+ assert_equal 3, res
530
+ assert_equal 'bar', read_buffer.get_string(0, 3)
531
+ end
532
+
533
+ def test_read_io_buffer_resize
534
+ r, w = UM.pipe
535
+ machine.write(w, 'foobar')
536
+ machine.close(w)
537
+
538
+ read_buffer = IO::Buffer.new(3)
539
+ res = machine.read(r, read_buffer, 6)
540
+ assert_equal 6, res
541
+ assert_equal 6, read_buffer.size
542
+ assert_equal 'foobar', read_buffer.get_string(0, res)
543
+
544
+ r, w = UM.pipe
545
+ machine.write(w, 'foobar')
546
+ machine.close(w)
547
+
548
+ read_buffer = IO::Buffer.new(3)
549
+ res = machine.read(r, read_buffer, 128, -1)
550
+ assert_equal 6, res
551
+ assert_equal 131, read_buffer.size
552
+ assert_equal 'foobar', read_buffer.get_string(3, res)
553
+ end
554
+
555
+ def test_read_invalid_buffer
556
+ r, _w = UM.pipe
557
+ assert_raises(UM::Error) {
558
+ machine.read(r, [])
559
+ }
560
+ end
561
+
562
+ def test_read_with_file_offset
563
+ fn = "/tmp/#{SecureRandom.hex}"
564
+ IO.write(fn, 'foobar')
565
+
566
+ fd = machine.open(fn, UM::O_RDONLY)
567
+ buffer = +''
568
+ result = machine.read(fd, buffer, 100, 0, 2)
569
+ assert_equal 4, result
570
+ assert_equal 'obar', buffer
571
+ ensure
572
+ machine.close(fd)
573
+ end
422
574
  end
423
575
 
424
576
  class ReadEachTest < UMBaseTest
@@ -569,11 +721,13 @@ class WriteTest < UMBaseTest
569
721
  r, w = IO.pipe
570
722
 
571
723
  assert_equal 0, machine.pending_count
572
- machine.write(w.fileno, 'foo')
724
+ res = machine.write(w.fileno, 'foo')
725
+ assert_equal 3, res
573
726
  assert_equal 0, machine.pending_count
574
727
  assert_equal 'foo', r.readpartial(3)
575
728
 
576
- machine.write(w.fileno, 'bar', 2)
729
+ res = machine.write(w.fileno, 'bar', 2)
730
+ assert_equal 2, res
577
731
  assert_equal 'ba', r.readpartial(3)
578
732
  end
579
733
 
@@ -586,6 +740,85 @@ class WriteTest < UMBaseTest
586
740
  end
587
741
  assert_equal 0, machine.pending_count
588
742
  end
743
+
744
+ def test_write_zero_length
745
+ r, w = IO.pipe
746
+
747
+ res = machine.write(w.fileno, '')
748
+ assert_equal 0, res
749
+
750
+ res = machine.write(w.fileno, 'bar', 0)
751
+ assert_equal 0, res
752
+
753
+ buf = IO::Buffer.new(3)
754
+ buf.set_string('baz')
755
+ res = machine.write(w.fileno, buf, 0, 0)
756
+ assert_equal 0, res
757
+
758
+ w.close
759
+ assert_equal '', r.read
760
+ end
761
+
762
+ def test_write_io_buffer
763
+ r, w = UM.pipe
764
+
765
+ msg = 'Hello world'
766
+ write_buffer = IO::Buffer.new(msg.bytesize)
767
+ write_buffer.set_string(msg, 0)
768
+
769
+ machine.write(w, write_buffer)
770
+ machine.close(w)
771
+
772
+ str = +''
773
+ machine.read(r, str, 8192)
774
+ assert_equal msg, str
775
+ end
776
+
777
+ def test_write_io_buffer_with_len
778
+ r, w = UM.pipe
779
+ msg = 'Hello world'
780
+ write_buffer = IO::Buffer.new(msg.bytesize)
781
+ write_buffer.set_string(msg)
782
+
783
+ machine.write(w, write_buffer, 5)
784
+ machine.close(w)
785
+
786
+ str = +''
787
+ machine.read(r, str, 8192)
788
+ assert_equal 'Hello', str
789
+
790
+ r, w = UM.pipe
791
+ msg = 'Hello world'
792
+ write_buffer = IO::Buffer.new(msg.bytesize)
793
+ write_buffer.set_string(msg)
794
+
795
+ machine.write(w, write_buffer, -1)
796
+ machine.close(w)
797
+
798
+ str = +''
799
+ machine.read(r, str, 8192)
800
+ assert_equal 'Hello world', str
801
+ end
802
+
803
+ def test_write_invalid_buffer
804
+ _r, w = UM.pipe
805
+ assert_raises(UM::Error) {
806
+ machine.write(w, [])
807
+ }
808
+ end
809
+
810
+ def test_write_with_file_offset
811
+ fn = "/tmp/#{SecureRandom.hex}"
812
+ IO.write(fn, 'foobar')
813
+
814
+ fd = machine.open(fn, UM::O_WRONLY)
815
+ result = machine.write(fd, 'baz', -1, 2)
816
+ assert_equal 3, result
817
+ assert_equal 'fobazr', IO.read(fn)
818
+ ensure
819
+ machine.close(fd)
820
+ end
821
+
589
822
  end
590
823
 
591
824
  class WriteAsyncTest < UMBaseTest
@@ -596,7 +829,7 @@ class WriteAsyncTest < UMBaseTest
596
829
  machine.write_async(w.fileno, 'foo')
597
830
  assert_equal 1, machine.pending_count
598
831
 
599
- machine.snooze
832
+ machine.snooze while machine.pending_count > 0
600
833
  assert_equal 0, machine.pending_count
601
834
  assert_equal 'foo', r.readpartial(3)
602
835
  end
@@ -612,7 +845,7 @@ class WriteAsyncTest < UMBaseTest
612
845
  GC.start
613
846
  assert_equal 1, machine.pending_count
614
847
 
615
- machine.snooze
848
+ machine.snooze while machine.pending_count > 0
616
849
  assert_equal 0, machine.pending_count
617
850
  assert_equal "foo#{123}#{'bar' * 48}", r.readpartial(len)
618
851
  end
@@ -626,6 +859,54 @@ class WriteAsyncTest < UMBaseTest
626
859
  machine.snooze
627
860
  assert_equal 0, machine.pending_count
628
861
  end
862
+
863
+ def test_write_async_io_buffer
864
+ r, w = UM.pipe
865
+
866
+ msg = 'Hello world'
867
+ write_buffer = IO::Buffer.new(msg.bytesize)
868
+ write_buffer.set_string(msg)
869
+
870
+ machine.write_async(w, write_buffer)
871
+ machine.snooze while machine.pending_count > 0
872
+ machine.close(w)
873
+
874
+ str = +''
875
+ machine.read(r, str, 8192)
876
+ assert_equal msg, str
877
+ end
878
+
879
+ def test_write_async_invalid_buffer
880
+ _r, w = UM.pipe
881
+
882
+ assert_raises(UM::Error) { machine.write_async(w, []) }
883
+ end
884
+
885
+ def test_write_async_with_len
886
+ r, w = IO.pipe
887
+
888
+ assert_equal 0, machine.pending_count
889
+ machine.write_async(w.fileno, 'foobar', 4)
890
+
891
+ assert_equal 1, machine.pending_count
892
+ machine.snooze while machine.pending_count > 0
893
+ assert_equal 0, machine.pending_count
894
+ assert_equal 'foob', r.readpartial(6)
895
+ end
896
+
897
+ def test_write_async_with_file_offset
898
+ fn = "/tmp/#{SecureRandom.hex}"
899
+ IO.write(fn, 'foobar')
900
+
901
+ fd = machine.open(fn, UM::O_WRONLY)
902
+ machine.write_async(fd, 'baz', -1, 2)
903
+
904
+ assert_equal 1, machine.pending_count
905
+ machine.snooze while machine.pending_count > 0
906
+ assert_equal 'fobazr', IO.read(fn)
907
+ ensure
908
+ machine.close(fd)
909
+ end
629
910
  end
630
911
 
631
912
  class CloseTest < UMBaseTest
@@ -643,7 +924,7 @@ class CloseTest < UMBaseTest
643
924
  end
644
925
 
645
926
  def test_close_bad_fd
646
- r, w = IO.pipe
927
+ _r, w = IO.pipe
647
928
  machine.close(w.fileno)
648
929
 
649
930
  assert_raises(Errno::EBADF) { machine.close(w.fileno) }
@@ -902,6 +1183,78 @@ class SendTest < UMBaseTest
902
1183
  ensure
903
1184
  t&.kill
904
1185
  end
1186
+
1187
+
1188
+ def test_send_io_buffer
1189
+ @port = assign_port
1190
+ @server = TCPServer.open('127.0.0.1', @port)
1191
+
1192
+ t = Thread.new do
1193
+ conn = @server.accept
1194
+ str = conn.readpartial(42)
1195
+ conn.write("You said: #{str} (#{str.bytesize})")
1196
+ sleep
1197
+ end
1198
+
1199
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1200
+ res = machine.connect(fd, '127.0.0.1', @port)
1201
+ assert_equal 0, res
1202
+
1203
+ buffer = IO::Buffer.new(6)
1204
+ buffer.set_string('foobar')
1205
+ res = machine.send(fd, buffer, 6, 0)
1206
+ assert_equal 6, res
1207
+
1208
+ buf = +''
1209
+ res = machine.read(fd, buf, 42)
1210
+ assert_equal 20, res
1211
+ assert_equal 'You said: foobar (6)', buf
1212
+ ensure
1213
+ t&.kill
1214
+ @server&.close
1215
+ end
1216
+
1217
+ def test_send_io_buffer_negative_len
1218
+ t = Thread.new do
1219
+ conn = @server.accept
1220
+ str = conn.readpartial(42)
1221
+ conn.write("You said: #{str} (#{str.bytesize})")
1222
+ sleep
1223
+ end
1224
+
1225
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1226
+ res = machine.connect(fd, '127.0.0.1', @port)
1227
+ assert_equal 0, res
1228
+
1229
+ buffer = IO::Buffer.new(6)
1230
+ buffer.set_string('foobar')
1231
+ res = machine.send(fd, buffer, -1, 0)
1232
+ assert_equal 6, res
1233
+
1234
+ buf = +''
1235
+ res = machine.read(fd, buf, 42)
1236
+ assert_equal 20, res
1237
+ assert_equal 'You said: foobar (6)', buf
1238
+ ensure
1239
+ t&.kill
1240
+ end
1241
+
1242
+ def test_send_invalid_buffer
1243
+ t = Thread.new do
1244
+ conn = @server.accept
1245
+ str = conn.readpartial(42)
1246
+ conn.write("You said: #{str} (#{str.bytesize})")
1247
+ sleep
1248
+ end
1249
+
1250
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1251
+ res = machine.connect(fd, '127.0.0.1', @port)
1252
+ assert_equal 0, res
1253
+
1254
+ assert_raises(UM::Error) { machine.send(fd, [], -1, 0) }
1255
+ ensure
1256
+ t&.kill
1257
+ end
905
1258
  end
906
1259
 
907
1260
  class RecvTest < UMBaseTest
@@ -934,6 +1287,25 @@ class RecvTest < UMBaseTest
934
1287
  ensure
935
1288
  t&.kill
936
1289
  end
1290
+
1291
+ def test_recv_io_buffer
1292
+ t = Thread.new do
1293
+ conn = @server.accept
1294
+ conn.write('foobar')
1295
+ sleep
1296
+ end
1297
+
1298
+ fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1299
+ res = machine.connect(fd, '127.0.0.1', @port)
1300
+ assert_equal 0, res
1301
+
1302
+ buf = IO::Buffer.new(12)
1303
+ res = machine.recv(fd, buf, 12, 0)
1304
+ assert_equal 6, res
1305
+ assert_equal 'foobar', buf.get_string(0, 6)
1306
+ ensure
1307
+ t&.kill
1308
+ end
937
1309
  end
938
1310
 
939
1311
  class RecvEachTest < UMBaseTest
@@ -1070,8 +1442,6 @@ end
1070
1442
 
1071
1443
  class SynchronizeTest < UMBaseTest
1072
1444
  def test_synchronize_single
1073
- skip if !machine.respond_to?(:synchronize)
1074
-
1075
1445
  m = UM::Mutex.new
1076
1446
 
1077
1447
  buf = []
@@ -1087,7 +1457,6 @@ class SynchronizeTest < UMBaseTest
1087
1457
  end
1088
1458
 
1089
1459
  def test_synchronize_pair
1090
- skip if !machine.respond_to?(:synchronize)
1091
1460
  m = UM::Mutex.new
1092
1461
 
1093
1462
  buf = []
@@ -1119,12 +1488,29 @@ class SynchronizeTest < UMBaseTest
1119
1488
  assert_equal [11, 12, 13, 21, 22, 23], buf
1120
1489
  assert_equal 0, machine.pending_count
1121
1490
  end
1491
+
1492
+ def test_synchronize_multi
1493
+ mutex = UM::Mutex.new
1494
+ buf = []
1495
+ fibers = (1..8).map { |i|
1496
+ machine.spin do
1497
+ machine.synchronize(mutex) do
1498
+ buf << (i * 10) + 1
1499
+ machine.sleep(0.01)
1500
+ buf << (i * 10) + 2
1501
+ end
1502
+ machine.snooze
1503
+ buf << (i * 10) + 3
1504
+ end
1505
+ }
1506
+
1507
+ machine.join(*fibers)
1508
+ 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
1509
+ end
1122
1510
  end
1123
1511
 
1124
1512
  class QueueTest < UMBaseTest
1125
1513
  def test_push_pop_1
1126
- skip if !machine.respond_to?(:synchronize)
1127
-
1128
1514
  q = UM::Queue.new
1129
1515
  assert_equal 0, q.count
1130
1516
  machine.push(q, :foo)
@@ -1138,8 +1524,6 @@ class QueueTest < UMBaseTest
1138
1524
  end
1139
1525
 
1140
1526
  def test_push_pop_2
1141
- skip if !machine.respond_to?(:synchronize)
1142
-
1143
1527
  q = UM::Queue.new
1144
1528
  buf = []
1145
1529
 
@@ -1170,8 +1554,6 @@ class QueueTest < UMBaseTest
1170
1554
  end
1171
1555
 
1172
1556
  def test_push_pop_3
1173
- skip if !machine.respond_to?(:synchronize)
1174
-
1175
1557
  q = UM::Queue.new
1176
1558
  buf = []
1177
1559
 
@@ -1198,8 +1580,6 @@ class QueueTest < UMBaseTest
1198
1580
  end
1199
1581
 
1200
1582
  def test_push_pop_4
1201
- skip if !machine.respond_to?(:synchronize)
1202
-
1203
1583
  q = UM::Queue.new
1204
1584
  buf = []
1205
1585
 
@@ -1227,8 +1607,6 @@ class QueueTest < UMBaseTest
1227
1607
  end
1228
1608
 
1229
1609
  def test_push_shift_1
1230
- skip if !machine.respond_to?(:synchronize)
1231
-
1232
1610
  q = UM::Queue.new
1233
1611
 
1234
1612
  machine.push(q, :foo)
@@ -1241,8 +1619,6 @@ class QueueTest < UMBaseTest
1241
1619
  end
1242
1620
 
1243
1621
  def test_shift_shift_1
1244
- skip if !machine.respond_to?(:synchronize)
1245
-
1246
1622
  q = UM::Queue.new
1247
1623
 
1248
1624
  machine.unshift(q, :foo)
@@ -1254,6 +1630,15 @@ class QueueTest < UMBaseTest
1254
1630
  assert_equal :foo, machine.shift(q)
1255
1631
  end
1256
1632
 
1633
+ def test_shift_exception_value
1634
+ q = UM::Queue.new
1635
+ machine.unshift(q, Exception.new("foo"))
1636
+
1637
+ e = machine.shift(q)
1638
+ assert_kind_of Exception, e
1639
+ assert_equal "foo", e.message
1640
+ end
1641
+
1257
1642
  def test_cross_thread_push_shift
1258
1643
  q = UM::Queue.new
1259
1644
 
@@ -1277,6 +1662,42 @@ class QueueTest < UMBaseTest
1277
1662
 
1278
1663
  assert_equal [0, 1, 2], items
1279
1664
  end
1665
+
1666
+ def test_cross_thread_cross_queue_comms
1667
+ worker_queue = UM::Queue.new
1668
+
1669
+ buf = []
1670
+ t1 = Thread.new {
1671
+ m = UM.new
1672
+ (1..10).each {
1673
+ q = UM::Queue.new
1674
+ m.push(worker_queue, [q, it])
1675
+ res = m.shift(q)
1676
+ buf << res
1677
+ }
1678
+ }
1679
+
1680
+ t2 = Thread.new {
1681
+ m = UM.new
1682
+ loop do
1683
+ q, v = m.shift(worker_queue)
1684
+ break if q == :STOP
1685
+
1686
+ res = v * 10
1687
+ m.push(q, res)
1688
+ end
1689
+ }
1690
+
1691
+ t1.join
1692
+ q = UM::Queue.new
1693
+ machine.push(worker_queue, :STOP)
1694
+ t2.join
1695
+
1696
+ assert_equal (1..10).map { it * 10 }, buf
1697
+ ensure
1698
+ t1.kill rescue nil
1699
+ t2.kill rescue nil
1700
+ end
1280
1701
  end
1281
1702
 
1282
1703
  class OpenTest < UMBaseTest
@@ -1335,12 +1756,67 @@ class PipeTest < UMBaseTest
1335
1756
  end
1336
1757
  end
1337
1758
 
1759
+ class PidfdTest < UMBaseTest
1760
+ def test_pidfd_open
1761
+ pid = fork { exit 13 }
1762
+ fd = UM.pidfd_open(pid)
1763
+ assert_kind_of Integer, fd
1764
+
1765
+ pid2, status = machine.waitid(P_PIDFD, fd, UM::WEXITED)
1766
+ assert_equal pid, pid2
1767
+ assert_equal 13, status
1768
+ ensure
1769
+ machine.close(fd) rescue nil
1770
+ Process.wait(pid) rescue nil
1771
+ end
1772
+
1773
+ def test_pidfd_open_invalid_pid
1774
+ assert_raises(SystemCallError) { UM.pidfd_open(Process.pid + 1) }
1775
+ end
1776
+
1777
+ def test_pidfd_send_signal
1778
+ fd = UM.pidfd_open(Process.pid)
1779
+ buf = []
1780
+ trap('SIGUSR1') { buf << :SIGUSR1 }
1781
+
1782
+ ret = UM.pidfd_send_signal(fd, UM::SIGUSR1)
1783
+ assert_equal fd, ret
1784
+ sleep 0.01
1785
+
1786
+ assert_equal [:SIGUSR1], buf
1787
+ ensure
1788
+ machine.close(fd) rescue nil
1789
+ end
1790
+
1791
+ def test_pidfd_send_signal_test
1792
+ fd = UM.pidfd_open(Process.pid)
1793
+ buf = []
1794
+ trap('SIGUSR1') { buf << :SIGUSR1 }
1795
+
1796
+ ret = UM.pidfd_send_signal(fd, 0)
1797
+ assert_equal fd, ret
1798
+ sleep 0.01
1799
+ assert_equal [], buf
1800
+
1801
+ pid = fork { exit 13 }
1802
+ fd2 = UM.pidfd_open(pid)
1803
+ assert_kind_of Integer, fd2
1804
+
1805
+ Process.wait(pid)
1806
+ assert_raises(SystemCallError) { UM.pidfd_send_signal(fd2, 0) }
1807
+ ensure
1808
+ machine.close(fd) rescue nil
1809
+ machine.close(fd2) rescue nil
1810
+ Process.wait(pid) rescue nil
1811
+ end
1812
+ end
1813
+
1338
1814
  class PollTest < UMBaseTest
1339
1815
  def test_poll
1340
1816
  rfd, wfd = UM.pipe
1341
1817
 
1342
1818
  events = []
1343
- f1 = machine.spin do
1819
+ machine.spin do
1344
1820
  events << :pre
1345
1821
  events << machine.poll(rfd, UM::POLLIN)
1346
1822
  events << :post
@@ -1366,38 +1842,160 @@ class PollTest < UMBaseTest
1366
1842
  end
1367
1843
  end
1368
1844
 
1369
- class WaitTest < UMBaseTest
1370
- def test_waitpid
1371
- skip if UM.kernel_version < 607
1845
+ class SelectTest < UMBaseTest
1846
+ def test_select
1847
+ rfd1, wfd1 = UM.pipe
1848
+ rfd2, wfd2 = UM.pipe
1849
+
1850
+ events = []
1851
+ machine.spin do
1852
+ events << 1
1853
+ events << machine.select([rfd1, rfd2], [], [])
1854
+ events << 2
1855
+ events << machine.select([rfd1, rfd2], [], [])
1856
+ events << 3
1857
+ machine.snooze
1858
+ events << machine.select([], [wfd1, wfd2], [])
1859
+ events << 4
1860
+ end
1861
+
1862
+ machine.snooze
1863
+ assert_equal [1], events
1864
+
1865
+ machine.write(wfd1, 'foo')
1866
+ machine.snooze
1867
+ assert_equal [1, [[rfd1], [], []], 2], events
1868
+
1869
+ machine.write(wfd2, 'foo')
1870
+
1871
+ machine.snooze
1872
+ assert_equal [1, [[rfd1], [], []], 2, [[rfd1, rfd2], [], []], 3], events
1873
+
1874
+ machine.snooze
1875
+
1876
+ assert_equal [
1877
+ 1, [[rfd1], [], []],
1878
+ 2, [[rfd1, rfd2], [], []],
1879
+ 3, [[], [wfd1, wfd2], []],
1880
+ 4
1881
+ ], events
1882
+
1883
+ machine.close(rfd1)
1884
+ machine.close(rfd2)
1885
+ end
1886
+
1887
+ def test_select_single
1888
+ rfd1, wfd1 = UM.pipe
1889
+
1890
+ events = []
1891
+ machine.spin do
1892
+ events << 1
1893
+ events << machine.select([rfd1], [], [])
1894
+ events << 2
1895
+ machine.snooze
1896
+ events << machine.select([], [wfd1], [])
1897
+ events << 3
1898
+ end
1899
+
1900
+ machine.snooze
1901
+ assert_equal [1], events
1902
+
1903
+ machine.write(wfd1, 'foo')
1904
+ machine.snooze
1905
+ assert_equal [1, [[rfd1], [], []], 2], events
1906
+
1907
+ 3.times { machine.snooze }
1908
+ assert_equal [1, [[rfd1], [], []], 2, [[], [wfd1], []], 3], events
1909
+
1910
+ machine.close(rfd1)
1911
+ machine.close(wfd1)
1912
+ end
1913
+
1914
+ def test_select_empty
1915
+ ret = machine.select([], [], [])
1916
+ assert_equal [[], [], []], ret
1917
+ end
1918
+
1919
+ def test_select_bad_fd
1920
+ assert_raises(Errno::EBADF) { machine.select([9876, 9877], [], []) }
1921
+ end
1922
+ end
1372
1923
 
1924
+ class WaitidTest < UMBaseTest
1925
+ def test_waitid
1373
1926
  msg = 'hello from child'
1927
+ _rfd, wfd = UM.pipe
1928
+ child_pid = fork do
1929
+ m = UM.new
1930
+ m.write(wfd, msg)
1931
+ m.close(wfd)
1932
+ exit 42
1933
+ end
1374
1934
 
1375
- rfd, wfd = UM.pipe
1376
- pid = fork do
1935
+ pid, status = machine.waitid(UM::P_PID, child_pid, UM::WEXITED)
1936
+ assert_equal child_pid, pid
1937
+ assert_equal 42, status
1938
+ ensure
1939
+ Process.wait(child_pid) rescue nil
1940
+ end
1941
+
1942
+ def test_waitid_invalid_pid
1943
+ assert_raises(Errno::ECHILD) {
1944
+ machine.waitid(UM::P_PID, Process.pid + 1, UM::WEXITED)
1945
+ }
1946
+ end
1947
+
1948
+ def test_waitid_invalid_idtype
1949
+ assert_raises(Errno::EINVAL) {
1950
+ machine.waitid(1234, 0, UM::WEXITED)
1951
+ }
1952
+ end
1953
+
1954
+ def test_waitid_invalid_options
1955
+ assert_raises(Errno::EINVAL) {
1956
+ machine.waitid(P_ALL, 0, 1234)
1957
+ }
1958
+ end
1959
+
1960
+ def test_waitid_P_ALL
1961
+ msg = 'hello from child'
1962
+ _rfd, wfd = UM.pipe
1963
+ child_pid = fork do
1377
1964
  m = UM.new
1378
1965
  m.write(wfd, msg)
1379
1966
  m.close(wfd)
1380
1967
  exit 42
1381
1968
  end
1382
1969
 
1383
- ret = machine.waitpid(pid, UM::WEXITED)
1384
- assert_kind_of Array, ret
1385
- assert_equal [pid, 42], ret
1970
+ pid, status = machine.waitid(UM::P_ALL, 0, UM::WEXITED)
1971
+ assert_equal child_pid, pid
1972
+ assert_equal 42, status
1973
+ ensure
1974
+ Process.wait(child_pid) rescue nil
1975
+ end
1386
1976
 
1387
- buf = +''
1388
- ret = machine.read(rfd, buf, 8192)
1389
- assert_equal msg.bytesize, ret
1390
- assert_equal msg, buf
1977
+ def test_waitid_P_PGID
1978
+ msg = 'hello from child'
1979
+ _rfd, wfd = UM.pipe
1980
+ child_pid = fork do
1981
+ m = UM.new
1982
+ m.write(wfd, msg)
1983
+ m.close(wfd)
1984
+ exit 42
1985
+ end
1986
+
1987
+ pid, status = machine.waitid(UM::P_PGID, Process.getpgrp, UM::WEXITED)
1988
+ assert_equal child_pid, pid
1989
+ assert_equal 42, status
1391
1990
  ensure
1392
- Process.wait(pid) rescue Errno::ECHILD
1991
+ Process.wait(child_pid) rescue nil
1393
1992
  end
1394
1993
 
1395
- def test_waitpid_any
1396
- skip if UM.kernel_version < 607
1994
+ def test_waitid_status
1995
+ skip if !machine.respond_to?(:waitid_status)
1397
1996
 
1398
1997
  msg = 'hello from child'
1399
-
1400
- rfd, wfd = UM.pipe
1998
+ _rfd, wfd = UM.pipe
1401
1999
  pid = fork do
1402
2000
  m = UM.new
1403
2001
  m.write(wfd, msg)
@@ -1405,23 +2003,76 @@ class WaitTest < UMBaseTest
1405
2003
  exit 42
1406
2004
  end
1407
2005
 
1408
- ret = machine.waitpid(0, UM::WEXITED)
1409
- assert_kind_of Array, ret
1410
- assert_equal [pid, 42], ret
2006
+ status = machine.waitid_status(UM::P_PID, pid, UM::WEXITED)
2007
+ assert_kind_of Process::Status, status
2008
+ assert_equal pid, status.pid
2009
+ assert_equal 42, status.exitstatus
2010
+ ensure
2011
+ Process.wait(pid) rescue nil
2012
+ end
1411
2013
 
1412
- buf = +''
1413
- ret = machine.read(rfd, buf, 8192)
1414
- assert_equal msg.bytesize, ret
1415
- assert_equal msg, buf
2014
+ def test_waitid_status_invalid_pid
2015
+ skip if !machine.respond_to?(:waitid_status)
2016
+
2017
+ assert_raises(Errno::ECHILD) {
2018
+ machine.waitid_status(UM::P_PID, Process.pid + 1, UM::WEXITED)
2019
+ }
2020
+ end
2021
+
2022
+ def test_waitid_status_invalid_idtype
2023
+ skip if !machine.respond_to?(:waitid_status)
2024
+
2025
+ assert_raises(Errno::EINVAL) {
2026
+ machine.waitid_status(1234, 0, UM::WEXITED)
2027
+ }
2028
+ end
2029
+
2030
+ def test_waitid_status_invalid_options
2031
+ skip if !machine.respond_to?(:waitid_status)
1416
2032
 
2033
+ assert_raises(Errno::EINVAL) {
2034
+ machine.waitid_status(P_ALL, 0, 1234)
2035
+ }
2036
+ end
2037
+
2038
+ def test_waitid_status_P_ALL
2039
+ skip if !machine.respond_to?(:waitid_status)
2040
+
2041
+ msg = 'hello from child'
2042
+ _rfd, wfd = UM.pipe
2043
+ pid = fork do
2044
+ m = UM.new
2045
+ m.write(wfd, msg)
2046
+ m.close(wfd)
2047
+ exit 42
2048
+ end
2049
+
2050
+ status = machine.waitid_status(UM::P_ALL, 0, UM::WEXITED)
2051
+ assert_kind_of Process::Status, status
2052
+ assert_equal pid, status.pid
2053
+ assert_equal 42, status.exitstatus
1417
2054
  ensure
1418
- Process.wait(pid) rescue Errno::ECHILD
2055
+ Process.wait(pid) rescue nil
1419
2056
  end
1420
2057
 
1421
- def test_waitpid_bad_pid
1422
- skip if UM.kernel_version < 607
2058
+ def test_waitid_status_P_PGID
2059
+ skip if !machine.respond_to?(:waitid_status)
1423
2060
 
1424
- assert_raises(Errno::ECHILD) { machine.waitpid(1, UM::WEXITED) }
2061
+ msg = 'hello from child'
2062
+ _rfd, wfd = UM.pipe
2063
+ pid = fork do
2064
+ m = UM.new
2065
+ m.write(wfd, msg)
2066
+ m.close(wfd)
2067
+ exit 42
2068
+ end
2069
+
2070
+ status = machine.waitid_status(UM::P_PGID, Process.getpgrp, UM::WEXITED)
2071
+ assert_kind_of Process::Status, status
2072
+ assert_equal pid, status.pid
2073
+ assert_equal 42, status.exitstatus
2074
+ ensure
2075
+ Process.wait(pid) rescue nil
1425
2076
  end
1426
2077
  end
1427
2078
 
@@ -1509,7 +2160,7 @@ class ForkTest < UMBaseTest
1509
2160
  assert_equal 3, ret
1510
2161
  assert_equal 'foo', buf
1511
2162
  ensure
1512
- Process.wait(child_pid) rescue Errno::ECHILD
2163
+ Process.wait(child_pid) rescue nil
1513
2164
  end
1514
2165
  end
1515
2166
 
@@ -1573,3 +2224,20 @@ class SendBundleTest < UMBaseTest
1573
2224
  assert_equal strs.map(&:to_s).join, buf
1574
2225
  end
1575
2226
  end
2227
+
2228
+ class NonBlockTest < UMBaseTest
2229
+ def test_io_nonblock?
2230
+ assert_equal false, UM.io_nonblock?(STDIN)
2231
+ end
2232
+
2233
+ def test_io_set_nonblock
2234
+ r, _w = IO.pipe
2235
+ assert_equal true, UM.io_nonblock?(r)
2236
+
2237
+ UM.io_set_nonblock(r, false)
2238
+ assert_equal false, UM.io_nonblock?(r)
2239
+
2240
+ UM.io_set_nonblock(r, true)
2241
+ assert_equal true, UM.io_nonblock?(r)
2242
+ end
2243
+ end