uringmachine 0.21.0 → 0.22.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +14 -0
  4. data/TODO.md +144 -0
  5. data/benchmark/README.md +173 -0
  6. data/benchmark/bm_io_pipe.rb +70 -0
  7. data/benchmark/bm_io_socketpair.rb +71 -0
  8. data/benchmark/bm_mutex_cpu.rb +57 -0
  9. data/benchmark/bm_mutex_io.rb +64 -0
  10. data/benchmark/bm_pg_client.rb +109 -0
  11. data/benchmark/bm_queue.rb +76 -0
  12. data/benchmark/chart.png +0 -0
  13. data/benchmark/common.rb +135 -0
  14. data/benchmark/dns_client.rb +47 -0
  15. data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
  16. data/benchmark/run_bm.rb +8 -0
  17. data/benchmark/sqlite.rb +108 -0
  18. data/{examples/bm_write.rb → benchmark/write.rb} +4 -4
  19. data/ext/um/um.c +189 -100
  20. data/ext/um/um.h +36 -10
  21. data/ext/um/um_async_op.c +1 -1
  22. data/ext/um/um_class.c +87 -13
  23. data/ext/um/um_op.c +6 -0
  24. data/ext/um/um_sync.c +2 -2
  25. data/ext/um/um_utils.c +16 -0
  26. data/grant-2025/journal.md +118 -1
  27. data/grant-2025/tasks.md +48 -22
  28. data/lib/uringmachine/actor.rb +8 -0
  29. data/lib/uringmachine/dns_resolver.rb +1 -2
  30. data/lib/uringmachine/fiber_scheduler.rb +127 -81
  31. data/lib/uringmachine/version.rb +1 -1
  32. data/lib/uringmachine.rb +32 -3
  33. data/test/helper.rb +7 -18
  34. data/test/test_actor.rb +12 -3
  35. data/test/test_async_op.rb +10 -10
  36. data/test/test_fiber.rb +84 -1
  37. data/test/test_fiber_scheduler.rb +950 -47
  38. data/test/test_um.rb +297 -120
  39. data/uringmachine.gemspec +2 -1
  40. metadata +38 -16
  41. data/examples/bm_fileno.rb +0 -33
  42. data/examples/bm_queue.rb +0 -111
  43. data/examples/bm_side_running.rb +0 -83
  44. data/examples/bm_sqlite.rb +0 -89
  45. data/examples/dns_client.rb +0 -12
  46. /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
  47. /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
  48. /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
  49. /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
data/test/test_um.rb CHANGED
@@ -11,15 +11,15 @@ class UringMachineTest < Minitest::Test
11
11
  end
12
12
  end
13
13
 
14
- class EntriesTest < Minitest::Test
15
- def test_default_entries
14
+ class SizeTest < Minitest::Test
15
+ def test_default_size
16
16
  m = UM.new
17
- assert_equal 4096, m.entries
17
+ assert_equal 4096, m.size
18
18
  end
19
19
 
20
- def test_custom_entries_value
20
+ def test_custom_size_value
21
21
  m = UM.new(13)
22
- assert_equal 13, m.entries
22
+ assert_equal 13, m.size
23
23
  end
24
24
  end
25
25
 
@@ -73,15 +73,17 @@ class SubmitTest < UMBaseTest
73
73
  def test_submit
74
74
  _r, w = UM.pipe
75
75
 
76
+ assert_equal 0, machine.pending_fibers.size
76
77
  machine.write_async(w, 'foo')
77
78
  machine.write_async(w, 'bar')
78
79
  machine.write_async(w, 'baz')
80
+ assert_equal 0, machine.pending_fibers.size
79
81
 
80
- assert_equal 3, machine.pending_count
82
+ assert_equal 3, machine.metrics[:ops_pending]
81
83
  assert_equal 3, machine.submit
82
84
  assert_equal 0, machine.submit
83
85
  machine.snooze
84
- assert_equal 0, machine.pending_count
86
+ assert_equal 0, machine.metrics[:ops_pending]
85
87
  end
86
88
  end
87
89
 
@@ -109,7 +111,7 @@ end
109
111
 
110
112
  class SnoozeTest < UMBaseTest
111
113
  def test_snooze_while_sleeping_fiber
112
- machine.spin do
114
+ f = machine.spin do
113
115
  machine.sleep(0.1)
114
116
  end
115
117
 
@@ -122,6 +124,9 @@ class SnoozeTest < UMBaseTest
122
124
  machine.snooze
123
125
  t1 = monotonic_clock
124
126
  assert_in_range 0..0.001, t1 - t0
127
+ ensure
128
+ machine.schedule(f, nil)
129
+ machine.join(f)
125
130
  end
126
131
  end
127
132
 
@@ -133,7 +138,7 @@ class ScheduleTest < UMBaseTest
133
138
  buf << [21, x]
134
139
  machine.schedule(main, 21)
135
140
  buf << 22
136
- x = machine.yield
141
+ x = machine.switch
137
142
  buf << [23, x]
138
143
  end
139
144
 
@@ -152,11 +157,10 @@ class ScheduleTest < UMBaseTest
152
157
  def test_schedule_exception
153
158
  buf = []
154
159
  f = Fiber.new do
155
- # this should raise
156
- machine.yield
160
+ machine.yield # this should raise
157
161
  rescue Exception => e
158
162
  buf << e
159
- machine.yield
163
+ machine.switch
160
164
  end
161
165
 
162
166
  machine.schedule(f, nil)
@@ -176,19 +180,19 @@ class ScheduleTest < UMBaseTest
176
180
  e = CustomError.new
177
181
  f = Fiber.new do
178
182
  machine.schedule(main, e)
179
- machine.yield
183
+ machine.switch
180
184
  end
181
185
 
182
186
  machine.schedule(f, nil)
183
187
  t0 = monotonic_clock
184
188
 
185
189
  # the call to schedule means an op is checked out
186
- assert_equal 0, machine.pending_count
190
+ assert_equal 0, machine.metrics[:ops_pending]
187
191
  begin
188
192
  machine.sleep(1)
189
193
  rescue Exception => e2
190
194
  end
191
- assert_equal 0, machine.pending_count
195
+ assert_equal 0, machine.metrics[:ops_pending]
192
196
  t1 = monotonic_clock
193
197
 
194
198
  assert_equal e2, e
@@ -211,7 +215,7 @@ class ScheduleTest < UMBaseTest
211
215
  buf << 5
212
216
  end
213
217
 
214
- assert_equal 0, machine.pending_count
218
+ assert_equal 0, machine.metrics[:ops_pending]
215
219
  assert_equal [1, 2, 5], buf
216
220
  assert_kind_of TOError, e
217
221
  end
@@ -250,9 +254,9 @@ class ScheduleTest < UMBaseTest
250
254
  rescue => e
251
255
  end
252
256
 
253
- assert_equal 1, machine.pending_count
257
+ assert_equal 1, machine.metrics[:ops_pending]
254
258
  machine.sleep(0.01) # wait for cancelled CQEs
255
- assert_equal 0, machine.pending_count
259
+ assert_equal 0, machine.metrics[:ops_pending]
256
260
 
257
261
  assert_kind_of RuntimeError, e
258
262
  assert_equal 'hi', e.message
@@ -263,9 +267,9 @@ class ScheduleTest < UMBaseTest
263
267
 
264
268
  assert_equal 42, v
265
269
 
266
- assert_equal 1, machine.pending_count
270
+ assert_equal 1, machine.metrics[:ops_pending]
267
271
  machine.sleep 0.01 # wait for cancelled CQE
268
- assert_equal 0, machine.pending_count
272
+ assert_equal 0, machine.metrics[:ops_pending]
269
273
  end
270
274
 
271
275
  def test_timeout_with_no_timeout
@@ -274,9 +278,9 @@ class ScheduleTest < UMBaseTest
274
278
 
275
279
  assert_equal 3, v
276
280
 
277
- assert_equal 1, machine.pending_count
281
+ assert_equal 1, machine.metrics[:ops_pending]
278
282
  machine.sleep 0.01 # wait for cancelled CQE
279
- assert_equal 0, machine.pending_count
283
+ assert_equal 0, machine.metrics[:ops_pending]
280
284
  end
281
285
 
282
286
  class TO2Error < RuntimeError; end
@@ -289,7 +293,7 @@ class ScheduleTest < UMBaseTest
289
293
  machine.timeout(0.04, TOError) do
290
294
  machine.timeout(0.02, TO2Error) do
291
295
  machine.timeout(0.03, TO3Error) do
292
- buf << machine.pending_count
296
+ buf << machine.metrics[:ops_pending]
293
297
  machine.sleep(1)
294
298
  end
295
299
  end
@@ -297,9 +301,9 @@ class ScheduleTest < UMBaseTest
297
301
  rescue => e
298
302
  end
299
303
 
300
- assert_equal 2, machine.pending_count
304
+ assert_equal 2, machine.metrics[:ops_pending]
301
305
  machine.sleep(0.01) # wait for cancelled CQEs
302
- assert_equal 0, machine.pending_count
306
+ assert_equal 0, machine.metrics[:ops_pending]
303
307
 
304
308
  assert_kind_of TO2Error, e
305
309
  assert_equal [3], buf
@@ -309,9 +313,9 @@ end
309
313
  class SleepTest < UMBaseTest
310
314
  def test_sleep
311
315
  t0 = monotonic_clock
312
- assert_equal 0, machine.pending_count
316
+ assert_equal 0, machine.metrics[:ops_pending]
313
317
  res = machine.sleep(0.1)
314
- assert_equal 0, machine.pending_count
318
+ assert_equal 0, machine.metrics[:ops_pending]
315
319
  t1 = monotonic_clock
316
320
  assert_in_range 0.09..0.13, t1 - t0
317
321
  assert_equal 0.1, res
@@ -368,7 +372,7 @@ class PeriodicallyTest < UMBaseTest
368
372
  cancel = 0
369
373
 
370
374
  t0 = monotonic_clock
371
- assert_equal 0, machine.pending_count
375
+ assert_equal 0, machine.metrics[:ops_pending]
372
376
  begin
373
377
  machine.periodically(0.01) do
374
378
  count += 1
@@ -378,7 +382,7 @@ class PeriodicallyTest < UMBaseTest
378
382
  cancel = 1
379
383
  end
380
384
  machine.snooze
381
- assert_equal 0, machine.pending_count
385
+ assert_equal 0, machine.metrics[:ops_pending]
382
386
  t1 = monotonic_clock
383
387
  assert_in_range 0.05..0.09, t1 - t0
384
388
  assert_equal 5, count
@@ -390,7 +394,7 @@ class PeriodicallyTest < UMBaseTest
390
394
  cancel = 0
391
395
 
392
396
  t0 = monotonic_clock
393
- assert_equal 0, machine.pending_count
397
+ assert_equal 0, machine.metrics[:ops_pending]
394
398
  begin
395
399
  machine.timeout(0.05, Cancel) do
396
400
  machine.periodically(0.01) do
@@ -413,21 +417,21 @@ class StatsTest < UMBaseTest
413
417
  def test_op_counts
414
418
  _r, w = IO.pipe
415
419
 
416
- assert_equal 0, machine.pending_count
417
- assert_equal 0, machine.total_op_count
420
+ assert_equal 0, machine.metrics[:ops_pending]
421
+ assert_equal 0, machine.metrics[:total_ops]
418
422
  machine.write_async(w.fileno, 'foo')
419
- assert_equal 1, machine.pending_count
420
- assert_equal 1, machine.total_op_count
423
+ assert_equal 1, machine.metrics[:ops_pending]
424
+ assert_equal 1, machine.metrics[:total_ops]
421
425
  machine.snooze
422
- assert_equal 0, machine.pending_count
423
- assert_equal 1, machine.total_op_count
426
+ assert_equal 0, machine.metrics[:ops_pending]
427
+ assert_equal 1, machine.metrics[:total_ops]
424
428
 
425
429
  machine.write_async(w.fileno, 'foo')
426
- assert_equal 1, machine.pending_count
427
- assert_equal 2, machine.total_op_count
430
+ assert_equal 1, machine.metrics[:ops_pending]
431
+ assert_equal 2, machine.metrics[:total_ops]
428
432
  machine.snooze
429
- assert_equal 0, machine.pending_count
430
- assert_equal 2, machine.total_op_count
433
+ assert_equal 0, machine.metrics[:ops_pending]
434
+ assert_equal 2, machine.metrics[:total_ops]
431
435
  end
432
436
  end
433
437
 
@@ -437,9 +441,9 @@ class ReadTest < UMBaseTest
437
441
  w << 'foobar'
438
442
 
439
443
  buf = +''
440
- assert_equal 0, machine.pending_count
444
+ assert_equal 0, machine.metrics[:ops_pending]
441
445
  res = machine.read(r.fileno, buf, 3)
442
- assert_equal 0, machine.pending_count
446
+ assert_equal 0, machine.metrics[:ops_pending]
443
447
  assert_equal 3, res
444
448
  assert_equal 'foo', buf
445
449
 
@@ -461,7 +465,7 @@ class ReadTest < UMBaseTest
461
465
  assert_raises(Errno::EBADF) do
462
466
  machine.read(w.fileno, +'', 8192)
463
467
  end
464
- assert_equal 0, machine.pending_count
468
+ assert_equal 0, machine.metrics[:ops_pending]
465
469
  end
466
470
 
467
471
  def test_read_with_buffer_offset
@@ -560,7 +564,7 @@ class ReadTest < UMBaseTest
560
564
  end
561
565
 
562
566
  def test_read_with_file_offset
563
- fn = "/tmp/#{SecureRandom.hex}"
567
+ fn = "/tmp/um_#{SecureRandom.hex}"
564
568
  IO.write(fn, 'foobar')
565
569
 
566
570
  fd = machine.open(fn, UM::O_RDONLY)
@@ -570,6 +574,7 @@ class ReadTest < UMBaseTest
570
574
  assert_equal 'obar', buffer
571
575
  ensure
572
576
  machine.close(fd)
577
+ FileUtils.rm(fn) rescue nil
573
578
  end
574
579
  end
575
580
 
@@ -590,7 +595,7 @@ class ReadEachTest < UMBaseTest
590
595
  w << 'baz'
591
596
  machine.sleep 0.02
592
597
  w.close
593
- machine.yield
598
+ machine.switch
594
599
  end
595
600
 
596
601
  machine.schedule(f, nil)
@@ -600,7 +605,7 @@ class ReadEachTest < UMBaseTest
600
605
  end
601
606
 
602
607
  assert_equal ['foo', 'bar', 'baz'], bufs
603
- assert_equal 0, machine.pending_count
608
+ assert_equal 0, machine.metrics[:ops_pending]
604
609
  end
605
610
 
606
611
  # send once and close write fd
@@ -624,7 +629,7 @@ class ReadEachTest < UMBaseTest
624
629
 
625
630
  assert_kind_of RuntimeError, e
626
631
  assert_equal 'hi', e.message
627
- assert_equal 0, machine.pending_count
632
+ assert_equal 0, machine.metrics[:ops_pending]
628
633
  end
629
634
 
630
635
  # send once and leave write fd open
@@ -649,7 +654,7 @@ class ReadEachTest < UMBaseTest
649
654
  assert_equal 'hi', e.message
650
655
 
651
656
  machine.snooze # in case the final CQE has not yet arrived
652
- assert_equal 0, machine.pending_count
657
+ assert_equal 0, machine.metrics[:ops_pending]
653
658
  end
654
659
 
655
660
  # send twice
@@ -675,7 +680,7 @@ class ReadEachTest < UMBaseTest
675
680
  assert_equal 'hi', e.message
676
681
 
677
682
  machine.snooze # in case the final CQE has not yet arrived
678
- assert_equal 0, machine.pending_count
683
+ assert_equal 0, machine.metrics[:ops_pending]
679
684
  end
680
685
 
681
686
  def test_read_each_break
@@ -699,7 +704,7 @@ class ReadEachTest < UMBaseTest
699
704
 
700
705
  assert_equal ['foo'], bufs
701
706
  machine.snooze # in case the final CQE has not yet arrived
702
- assert_equal 0, machine.pending_count
707
+ assert_equal 0, machine.metrics[:ops_pending]
703
708
  ensure
704
709
  t&.kill
705
710
  end
@@ -720,10 +725,10 @@ class WriteTest < UMBaseTest
720
725
  def test_write
721
726
  r, w = IO.pipe
722
727
 
723
- assert_equal 0, machine.pending_count
728
+ assert_equal 0, machine.metrics[:ops_pending]
724
729
  res = machine.write(w.fileno, 'foo')
725
730
  assert_equal 3, res
726
- assert_equal 0, machine.pending_count
731
+ assert_equal 0, machine.metrics[:ops_pending]
727
732
  assert_equal 'foo', r.readpartial(3)
728
733
 
729
734
  res = machine.write(w.fileno, 'bar', 2)
@@ -734,11 +739,11 @@ class WriteTest < UMBaseTest
734
739
  def test_write_bad_fd
735
740
  r, _w = IO.pipe
736
741
 
737
- assert_equal 0, machine.pending_count
742
+ assert_equal 0, machine.metrics[:ops_pending]
738
743
  assert_raises(Errno::EBADF) do
739
744
  machine.write(r.fileno, 'foo')
740
745
  end
741
- assert_equal 0, machine.pending_count
746
+ assert_equal 0, machine.metrics[:ops_pending]
742
747
  end
743
748
 
744
749
  def test_write_zero_length
@@ -808,7 +813,7 @@ class WriteTest < UMBaseTest
808
813
  end
809
814
 
810
815
  def test_write_with_file_offset
811
- fn = "/tmp/#{SecureRandom.hex}"
816
+ fn = "/tmp/um_#{SecureRandom.hex}"
812
817
  IO.write(fn, 'foobar')
813
818
 
814
819
  fd = machine.open(fn, UM::O_WRONLY)
@@ -817,47 +822,47 @@ class WriteTest < UMBaseTest
817
822
  assert_equal 'fobazr', IO.read(fn)
818
823
  ensure
819
824
  machine.close(fd)
825
+ FileUtils.rm(fn) rescue nil
820
826
  end
821
-
822
827
  end
823
828
 
824
829
  class WriteAsyncTest < UMBaseTest
825
830
  def test_write_async
826
831
  r, w = IO.pipe
827
832
 
828
- assert_equal 0, machine.pending_count
833
+ assert_equal 0, machine.metrics[:ops_pending]
829
834
  machine.write_async(w.fileno, 'foo')
830
- assert_equal 1, machine.pending_count
835
+ assert_equal 1, machine.metrics[:ops_pending]
831
836
 
832
- machine.snooze while machine.pending_count > 0
833
- assert_equal 0, machine.pending_count
837
+ machine.snooze while machine.metrics[:ops_pending] > 0
838
+ assert_equal 0, machine.metrics[:ops_pending]
834
839
  assert_equal 'foo', r.readpartial(3)
835
840
  end
836
841
 
837
842
  def test_write_async_dynamic_string
838
843
  r, w = IO.pipe
839
844
 
840
- assert_equal 0, machine.pending_count
845
+ assert_equal 0, machine.metrics[:ops_pending]
841
846
  str = "foo#{123}#{'bar' * 48}"
842
847
  len = str.bytesize
843
848
  machine.write_async(w.fileno, str)
844
849
  str = nil
845
- GC.start
846
- assert_equal 1, machine.pending_count
850
+ # GC.start
851
+ assert_equal 1, machine.metrics[:ops_pending]
847
852
 
848
- machine.snooze while machine.pending_count > 0
849
- assert_equal 0, machine.pending_count
853
+ machine.snooze while machine.metrics[:ops_pending] > 0
854
+ assert_equal 0, machine.metrics[:ops_pending]
850
855
  assert_equal "foo#{123}#{'bar' * 48}", r.readpartial(len)
851
856
  end
852
857
 
853
858
  def test_write_async_bad_fd
854
859
  r, _w = IO.pipe
855
860
 
856
- assert_equal 0, machine.pending_count
861
+ assert_equal 0, machine.metrics[:ops_pending]
857
862
  machine.write_async(r.fileno, 'foo')
858
- assert_equal 1, machine.pending_count
863
+ assert_equal 1, machine.metrics[:ops_pending]
859
864
  machine.snooze
860
- assert_equal 0, machine.pending_count
865
+ assert_equal 0, machine.metrics[:ops_pending]
861
866
  end
862
867
 
863
868
  def test_write_async_io_buffer
@@ -868,7 +873,7 @@ class WriteAsyncTest < UMBaseTest
868
873
  write_buffer.set_string(msg)
869
874
 
870
875
  machine.write_async(w, write_buffer)
871
- machine.snooze while machine.pending_count > 0
876
+ machine.snooze while machine.metrics[:ops_pending] > 0
872
877
  machine.close(w)
873
878
 
874
879
  str = +''
@@ -885,27 +890,28 @@ class WriteAsyncTest < UMBaseTest
885
890
  def test_write_async_with_len
886
891
  r, w = IO.pipe
887
892
 
888
- assert_equal 0, machine.pending_count
893
+ assert_equal 0, machine.metrics[:ops_pending]
889
894
  machine.write_async(w.fileno, 'foobar', 4)
890
895
 
891
- assert_equal 1, machine.pending_count
892
- machine.snooze while machine.pending_count > 0
893
- assert_equal 0, machine.pending_count
896
+ assert_equal 1, machine.metrics[:ops_pending]
897
+ machine.snooze while machine.metrics[:ops_pending] > 0
898
+ assert_equal 0, machine.metrics[:ops_pending]
894
899
  assert_equal 'foob', r.readpartial(6)
895
900
  end
896
901
 
897
902
  def test_write_async_with_file_offset
898
- fn = "/tmp/#{SecureRandom.hex}"
903
+ fn = "/tmp/um_#{SecureRandom.hex}"
899
904
  IO.write(fn, 'foobar')
900
905
 
901
906
  fd = machine.open(fn, UM::O_WRONLY)
902
907
  machine.write_async(fd, 'baz', -1, 2)
903
908
 
904
- assert_equal 1, machine.pending_count
905
- machine.snooze while machine.pending_count > 0
909
+ assert_equal 1, machine.metrics[:ops_pending]
910
+ machine.snooze while machine.metrics[:ops_pending] > 0
906
911
  assert_equal 'fobazr', IO.read(fn)
907
912
  ensure
908
913
  machine.close(fd)
914
+ FileUtils.rm(fn) rescue nil
909
915
  end
910
916
  end
911
917
 
@@ -915,9 +921,9 @@ class CloseTest < UMBaseTest
915
921
  machine.write(w.fileno, 'foo')
916
922
  assert_equal 'foo', r.readpartial(3)
917
923
 
918
- assert_equal 0, machine.pending_count
924
+ assert_equal 0, machine.metrics[:ops_pending]
919
925
  machine.close(w.fileno)
920
- assert_equal 0, machine.pending_count
926
+ assert_equal 0, machine.metrics[:ops_pending]
921
927
  assert_equal '', r.read
922
928
 
923
929
  assert_raises(Errno::EBADF) { machine.close(w.fileno) }
@@ -937,18 +943,18 @@ class CloseAsyncTest < UMBaseTest
937
943
  machine.write(w.fileno, 'foo')
938
944
  assert_equal 'foo', r.readpartial(3)
939
945
 
940
- assert_equal 0, machine.pending_count
941
- machine.close_async(w.fileno)
942
- assert_equal 1, machine.pending_count
946
+ assert_equal 0, machine.metrics[:ops_pending]
947
+ machine.close_async(w.fileno) # fire and forget
948
+ assert_equal 1, machine.metrics[:ops_pending]
943
949
  machine.snooze
944
- assert_equal 0, machine.pending_count
950
+ assert_equal 0, machine.metrics[:ops_pending]
945
951
  assert_equal '', r.read
946
952
  end
947
953
  end
948
954
 
949
955
  class ShutdownTest < UMBaseTest
950
956
  def test_shutdown
951
- c_fd, s_fd = make_socket_pair
957
+ c_fd, s_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
952
958
  res = @machine.send(c_fd, 'abc', 3, 0)
953
959
  assert_equal 3, res
954
960
 
@@ -980,7 +986,7 @@ end
980
986
 
981
987
  class ShutdownAsyncTest < UMBaseTest
982
988
  def test_shutdown_async
983
- c_fd, s_fd = make_socket_pair
989
+ c_fd, s_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
984
990
  res = @machine.send(c_fd, 'abc', 3, 0)
985
991
  assert_equal 3, res
986
992
 
@@ -1024,9 +1030,9 @@ class AcceptTest < UMBaseTest
1024
1030
  def test_accept
1025
1031
  conn = TCPSocket.new('127.0.0.1', @port)
1026
1032
 
1027
- assert_equal 0, machine.pending_count
1033
+ assert_equal 0, machine.metrics[:ops_pending]
1028
1034
  fd = machine.accept(@server.fileno)
1029
- assert_equal 0, machine.pending_count
1035
+ assert_equal 0, machine.metrics[:ops_pending]
1030
1036
  assert_kind_of Integer, fd
1031
1037
  assert fd > 0
1032
1038
 
@@ -1063,7 +1069,7 @@ class AcceptEachTest < UMBaseTest
1063
1069
  end
1064
1070
 
1065
1071
  assert_equal 3, count
1066
- assert_equal 0, machine.pending_count
1072
+ assert_equal 0, machine.metrics[:ops_pending]
1067
1073
  ensure
1068
1074
  t&.kill
1069
1075
  end
@@ -1098,9 +1104,9 @@ end
1098
1104
 
1099
1105
  class SocketTest < UMBaseTest
1100
1106
  def test_socket
1101
- assert_equal 0, machine.pending_count
1107
+ assert_equal 0, machine.metrics[:ops_pending]
1102
1108
  fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0);
1103
- assert_equal 0, machine.pending_count
1109
+ assert_equal 0, machine.metrics[:ops_pending]
1104
1110
  assert_kind_of Integer, fd
1105
1111
  assert fd > 0
1106
1112
 
@@ -1128,9 +1134,9 @@ class ConnectTest < UMBaseTest
1128
1134
  end
1129
1135
 
1130
1136
  fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
1131
- assert_equal 0, machine.pending_count
1137
+ assert_equal 0, machine.metrics[:ops_pending]
1132
1138
  res = machine.connect(fd, '127.0.0.1', @port)
1133
- assert_equal 0, machine.pending_count
1139
+ assert_equal 0, machine.metrics[:ops_pending]
1134
1140
  assert_equal 0, res
1135
1141
 
1136
1142
  buf = +''
@@ -1143,9 +1149,9 @@ class ConnectTest < UMBaseTest
1143
1149
 
1144
1150
  def test_connect_with_bad_addr
1145
1151
  fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0);
1146
- assert_equal 0, machine.pending_count
1152
+ assert_equal 0, machine.metrics[:ops_pending]
1147
1153
  assert_raises(Errno::ENETUNREACH) { machine.connect(fd, 'a.b.c.d', @port) }
1148
- assert_equal 0, machine.pending_count
1154
+ assert_equal 0, machine.metrics[:ops_pending]
1149
1155
  end
1150
1156
  end
1151
1157
 
@@ -1361,11 +1367,11 @@ class BindTest < UMBaseTest
1361
1367
  end
1362
1368
 
1363
1369
  def test_bind
1364
- assert_equal 0, machine.pending_count
1370
+ assert_equal 0, machine.metrics[:ops_pending]
1365
1371
  fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0)
1366
1372
  res = machine.bind(fd, '127.0.0.1', @port)
1367
1373
  assert_equal 0, res
1368
- assert_equal 0, machine.pending_count
1374
+ assert_equal 0, machine.metrics[:ops_pending]
1369
1375
 
1370
1376
  peer = UDPSocket.new
1371
1377
  peer.connect('127.0.0.1', @port)
@@ -1378,13 +1384,13 @@ class BindTest < UMBaseTest
1378
1384
  end
1379
1385
 
1380
1386
  def test_bind_invalid_args
1381
- assert_equal 0, machine.pending_count
1387
+ assert_equal 0, machine.metrics[:ops_pending]
1382
1388
 
1383
1389
  fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0)
1384
1390
  assert_raises(Errno::EACCES) { machine.bind(fd, 'foo.bar.baz', 3) }
1385
1391
  assert_raises(Errno::EBADF) { machine.bind(-3, '127.0.01', 1234) }
1386
1392
 
1387
- assert_equal 0, machine.pending_count
1393
+ assert_equal 0, machine.metrics[:ops_pending]
1388
1394
  end
1389
1395
  end
1390
1396
 
@@ -1399,7 +1405,7 @@ class ListenTest < UMBaseTest
1399
1405
  machine.bind(fd, '127.0.0.1', @port)
1400
1406
  res = machine.listen(fd, 5)
1401
1407
  assert_equal 0, res
1402
- assert_equal 0, machine.pending_count
1408
+ assert_equal 0, machine.metrics[:ops_pending]
1403
1409
 
1404
1410
  conn = nil
1405
1411
  t = Thread.new do
@@ -1453,12 +1459,11 @@ class SynchronizeTest < UMBaseTest
1453
1459
  end
1454
1460
 
1455
1461
  assert_equal [1, 2], buf
1456
- assert_equal 0, machine.pending_count
1462
+ assert_equal 0, machine.metrics[:ops_pending]
1457
1463
  end
1458
1464
 
1459
1465
  def test_synchronize_pair
1460
1466
  m = UM::Mutex.new
1461
-
1462
1467
  buf = []
1463
1468
 
1464
1469
  f1 = Fiber.new do
@@ -1468,7 +1473,7 @@ class SynchronizeTest < UMBaseTest
1468
1473
  buf << 12
1469
1474
  end
1470
1475
  buf << 13
1471
- machine.yield
1476
+ machine.switch
1472
1477
  end
1473
1478
 
1474
1479
  f2 = Fiber.new do
@@ -1478,7 +1483,7 @@ class SynchronizeTest < UMBaseTest
1478
1483
  buf << 22
1479
1484
  end
1480
1485
  buf << 23
1481
- machine.yield
1486
+ machine.switch
1482
1487
  end
1483
1488
 
1484
1489
  machine.schedule(f1, nil)
@@ -1486,7 +1491,7 @@ class SynchronizeTest < UMBaseTest
1486
1491
 
1487
1492
  machine.sleep(0.03)
1488
1493
  assert_equal [11, 12, 13, 21, 22, 23], buf
1489
- assert_equal 0, machine.pending_count
1494
+ assert_equal 0, machine.metrics[:ops_pending]
1490
1495
  end
1491
1496
 
1492
1497
  def test_synchronize_multi
@@ -1537,12 +1542,12 @@ class QueueTest < UMBaseTest
1537
1542
 
1538
1543
  machine.snooze
1539
1544
  assert_equal [], buf
1540
- assert_equal 2, machine.pending_count
1545
+ assert_equal 2, machine.metrics[:ops_pending]
1541
1546
 
1542
1547
  machine.push(q, :foo)
1543
1548
  assert_equal 1, q.count
1544
1549
  machine.snooze
1545
- assert_equal 1, machine.pending_count
1550
+ assert_equal 1, machine.metrics[:ops_pending]
1546
1551
  assert_equal [[1, :foo]], buf
1547
1552
 
1548
1553
  machine.push(q, :bar)
@@ -1563,13 +1568,13 @@ class QueueTest < UMBaseTest
1563
1568
 
1564
1569
  f1 = Fiber.new do
1565
1570
  buf << [1, machine.pop(q)]
1566
- machine.yield
1571
+ machine.switch
1567
1572
  end
1568
1573
  machine.schedule(f1, nil)
1569
1574
 
1570
1575
  f2 = Fiber.new do
1571
1576
  buf << [2, machine.pop(q)]
1572
- machine.yield
1577
+ machine.switch
1573
1578
  end
1574
1579
  machine.schedule(f2, nil)
1575
1580
 
@@ -1588,13 +1593,13 @@ class QueueTest < UMBaseTest
1588
1593
 
1589
1594
  f1 = Fiber.new do
1590
1595
  buf << [1, machine.pop(q)]
1591
- machine.yield
1596
+ machine.switch
1592
1597
  end
1593
1598
  machine.schedule(f1, nil)
1594
1599
 
1595
1600
  f2 = Fiber.new do
1596
1601
  buf << [2, machine.pop(q)]
1597
- machine.yield
1602
+ machine.switch
1598
1603
  end
1599
1604
  machine.schedule(f2, nil)
1600
1605
 
@@ -1701,38 +1706,40 @@ class QueueTest < UMBaseTest
1701
1706
  end
1702
1707
 
1703
1708
  class OpenTest < UMBaseTest
1704
- PATH = '/tmp/um_open_test'
1705
-
1706
1709
  def setup
1707
1710
  super
1708
- FileUtils.rm(PATH, force: true)
1711
+ @fn = "/tmp/um_#{SecureRandom.hex}"
1712
+ end
1713
+
1714
+ def teardown
1715
+ FileUtils.rm(@fn) rescue nil
1709
1716
  end
1710
1717
 
1711
1718
  def test_open
1712
- fd = machine.open(PATH, UM::O_CREAT | UM::O_WRONLY)
1719
+ fd = machine.open(@fn, UM::O_CREAT | UM::O_WRONLY)
1713
1720
  assert_kind_of Integer, fd
1714
- assert File.file?(PATH)
1721
+ assert File.file?(@fn)
1715
1722
 
1716
1723
  machine.write(fd, 'foo')
1717
1724
  machine.close(fd)
1718
1725
 
1719
- assert_equal 'foo', IO.read(PATH)
1726
+ assert_equal 'foo', IO.read(@fn)
1720
1727
  end
1721
1728
 
1722
1729
  def test_open_with_block
1723
- res = machine.open(PATH, UM::O_CREAT | UM::O_WRONLY) do |fd|
1730
+ res = machine.open(@fn, UM::O_CREAT | UM::O_WRONLY) do |fd|
1724
1731
  machine.write(fd, 'bar')
1725
1732
  fd
1726
1733
  end
1727
1734
 
1728
1735
  assert_kind_of Integer, res
1729
1736
  assert_raises(Errno::EBADF) { machine.close(res) }
1730
- assert_equal 'bar', IO.read(PATH)
1737
+ assert_equal 'bar', IO.read(@fn)
1731
1738
  end
1732
1739
 
1733
1740
  def test_open_bad_arg
1734
- assert_raises(Errno::ENOENT) { machine.open(PATH, UM::O_RDONLY) }
1735
- assert_raises(Errno::ENOENT) { machine.open(PATH, UM::O_RDONLY) {} }
1741
+ assert_raises(Errno::ENOENT) { machine.open(@fn, UM::O_RDONLY) }
1742
+ assert_raises(Errno::ENOENT) { machine.open(@fn, UM::O_RDONLY) {} }
1736
1743
  end
1737
1744
  end
1738
1745
 
@@ -1756,6 +1763,26 @@ class PipeTest < UMBaseTest
1756
1763
  end
1757
1764
  end
1758
1765
 
1766
+ class SocketpairTest < UMBaseTest
1767
+ def test_socketpair
1768
+ rfd, wfd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
1769
+ ret = machine.write(wfd, 'foo')
1770
+ assert_equal 3, ret
1771
+
1772
+ ret = machine.close(wfd)
1773
+ assert_equal wfd, ret
1774
+
1775
+ buf = +''
1776
+ ret = machine.read(rfd, buf, 8192)
1777
+
1778
+ assert_equal 3, ret
1779
+ assert_equal 'foo', buf
1780
+
1781
+ ret = machine.close(rfd)
1782
+ assert_equal rfd, ret
1783
+ end
1784
+ end
1785
+
1759
1786
  class PidfdTest < UMBaseTest
1760
1787
  def test_pidfd_open
1761
1788
  pid = fork { exit 13 }
@@ -2167,7 +2194,7 @@ end
2167
2194
  class SendBundleTest < UMBaseTest
2168
2195
  def setup
2169
2196
  super
2170
- @client_fd, @server_fd = make_socket_pair
2197
+ @client_fd, @server_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
2171
2198
  end
2172
2199
 
2173
2200
  def test_send_bundle_splat
@@ -2241,3 +2268,153 @@ class NonBlockTest < UMBaseTest
2241
2268
  assert_equal true, UM.io_nonblock?(r)
2242
2269
  end
2243
2270
  end
2271
+
2272
+ class MetricsTest < UMBaseTest
2273
+ def test_metrics_empty
2274
+ assert_equal({
2275
+ size: 4096,
2276
+ total_ops: 0,
2277
+ total_switches: 0,
2278
+ total_waits: 0,
2279
+ ops_pending: 0,
2280
+ ops_unsubmitted: 0,
2281
+ ops_runqueue: 0,
2282
+ ops_free: 0,
2283
+ ops_transient: 0
2284
+ }, machine.metrics)
2285
+ end
2286
+
2287
+ def test_metrics_size
2288
+ m = UM.new(13)
2289
+ assert_equal 13, m.metrics[:size]
2290
+ end
2291
+
2292
+ def test_metrics_total_counters
2293
+ r, w = UM.pipe
2294
+ machine.write(w, 'foo')
2295
+ assert_equal 1, machine.metrics[:total_ops]
2296
+ assert_equal 1, machine.metrics[:total_switches]
2297
+ assert_equal 1, machine.metrics[:total_waits]
2298
+ machine.write_async(w, 'bar')
2299
+ assert_equal 2, machine.metrics[:total_ops]
2300
+ assert_equal 1, machine.metrics[:total_switches]
2301
+ assert_equal 1, machine.metrics[:total_waits]
2302
+ machine.snooze
2303
+ assert_equal 2, machine.metrics[:total_ops]
2304
+ assert_equal 2, machine.metrics[:total_switches]
2305
+ assert_equal 2, machine.metrics[:total_waits]
2306
+ machine.close(w)
2307
+ assert_equal 3, machine.metrics[:total_ops]
2308
+ assert_equal 3, machine.metrics[:total_switches]
2309
+ assert_equal 3, machine.metrics[:total_waits]
2310
+ ensure
2311
+ machine.close(r) rescue nil
2312
+ machine.close(w) rescue nil
2313
+ end
2314
+
2315
+ def test_metrics_total_switches
2316
+ f1 = machine.spin { 3.times { machine.snooze } }
2317
+ f2 = machine.spin { machine.sleep(0.01) }
2318
+ assert_equal 0, machine.metrics[:total_switches]
2319
+ machine.snooze
2320
+ assert_equal 3, machine.metrics[:total_switches]
2321
+ machine.snooze
2322
+ assert_equal 5, machine.metrics[:total_switches]
2323
+ machine.join(f2)
2324
+ assert_equal 9, machine.metrics[:total_switches]
2325
+ ensure
2326
+ machine.join(f1, f2)
2327
+ end
2328
+
2329
+ def test_metrics_total_waits
2330
+ r, w = UM.pipe
2331
+
2332
+ machine.sleep(0.001)
2333
+ assert_equal 1, machine.metrics[:total_waits]
2334
+
2335
+ machine.write_async(w, 'foo')
2336
+ assert_equal 1, machine.metrics[:total_waits]
2337
+
2338
+ res = machine.read(r, +'', 3)
2339
+ assert_equal 2, machine.metrics[:total_waits]
2340
+ assert_equal 3, res
2341
+
2342
+ machine.close_async(w)
2343
+ assert_equal 2, machine.metrics[:total_waits]
2344
+
2345
+ machine.snooze
2346
+ assert_equal 3, machine.metrics[:total_waits]
2347
+ ensure
2348
+ machine.close(r) rescue nil
2349
+ machine.close(w) rescue nil
2350
+ end
2351
+
2352
+ def ops_metrics
2353
+ machine.metrics.values_at(
2354
+ :ops_pending,
2355
+ :ops_unsubmitted,
2356
+ :ops_runqueue,
2357
+ :ops_free,
2358
+ :ops_transient
2359
+ )
2360
+ end
2361
+
2362
+ def test_metrics_ops
2363
+ r, w = UM.pipe
2364
+
2365
+ f = machine.spin { machine.sleep(0.001) }
2366
+ assert_equal [0, 0, 1, 0, 0], ops_metrics
2367
+ machine.snooze
2368
+ assert_equal [1, 1, 0, 2, 0], ops_metrics
2369
+ machine.submit
2370
+ assert_equal [1, 0, 0, 2, 0], ops_metrics
2371
+ machine.join(f)
2372
+ assert_equal [0, 0, 0, 2, 0], ops_metrics
2373
+
2374
+ machine.write_async(w, 'foo')
2375
+ assert_equal [1, 1, 0, 1, 1], ops_metrics
2376
+ machine.submit
2377
+ assert_equal [1, 0, 0, 1, 1], ops_metrics
2378
+ machine.snooze
2379
+ assert_equal [0, 0, 0, 2, 0], ops_metrics
2380
+
2381
+ machine.write_async(w, 'foo')
2382
+ assert_equal [1, 1, 0, 1, 1], ops_metrics
2383
+ machine.snooze
2384
+ assert_equal [0, 0, 0, 2, 0], ops_metrics
2385
+ ensure
2386
+ machine.join(f)
2387
+ end
2388
+ end
2389
+
2390
+ class ProfileModeTest < UMBaseTest
2391
+ def test_profile_mode_empty
2392
+ assert_equal false, machine.profile?
2393
+ assert_equal([
2394
+ :size, :total_ops, :total_switches, :total_waits, :ops_pending,
2395
+ :ops_unsubmitted, :ops_runqueue, :ops_free, :ops_transient
2396
+ ], machine.metrics.keys)
2397
+
2398
+ machine.profile(true)
2399
+ assert_equal true, machine.profile?
2400
+ assert_equal([
2401
+ :size, :total_ops, :total_switches, :total_waits, :ops_pending,
2402
+ :ops_unsubmitted, :ops_runqueue, :ops_free, :ops_transient,
2403
+ :time_total_cpu, :time_total_wait,
2404
+ ], machine.metrics.keys)
2405
+
2406
+ machine.profile(false)
2407
+ assert_equal false, machine.profile?
2408
+ assert_equal([
2409
+ :size, :total_ops, :total_switches, :total_waits, :ops_pending,
2410
+ :ops_unsubmitted, :ops_runqueue, :ops_free, :ops_transient
2411
+ ], machine.metrics.keys)
2412
+ end
2413
+
2414
+ def test_profile_mode_total_times
2415
+ machine.profile(true)
2416
+ machine.sleep(0.01)
2417
+ assert_in_range 0.0..0.0005, machine.metrics[:time_total_cpu]
2418
+ assert_in_range 0.01..0.015, machine.metrics[:time_total_wait]
2419
+ end
2420
+ end