uringmachine 0.20.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -4
  3. data/.rubocop.yml +2 -0
  4. data/CHANGELOG.md +34 -0
  5. data/TODO.md +132 -26
  6. data/benchmark/README.md +173 -0
  7. data/benchmark/bm_io_pipe.rb +70 -0
  8. data/benchmark/bm_io_socketpair.rb +71 -0
  9. data/benchmark/bm_mutex_cpu.rb +57 -0
  10. data/benchmark/bm_mutex_io.rb +64 -0
  11. data/benchmark/bm_pg_client.rb +109 -0
  12. data/benchmark/bm_queue.rb +76 -0
  13. data/benchmark/chart.png +0 -0
  14. data/benchmark/common.rb +135 -0
  15. data/benchmark/dns_client.rb +47 -0
  16. data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
  17. data/benchmark/run_bm.rb +8 -0
  18. data/benchmark/sqlite.rb +108 -0
  19. data/{examples/bm_write.rb → benchmark/write.rb} +6 -3
  20. data/ext/um/extconf.rb +1 -1
  21. data/ext/um/um.c +404 -95
  22. data/ext/um/um.h +77 -24
  23. data/ext/um/um_async_op.c +2 -2
  24. data/ext/um/um_class.c +168 -18
  25. data/ext/um/um_op.c +43 -0
  26. data/ext/um/um_sync.c +10 -16
  27. data/ext/um/um_utils.c +16 -0
  28. data/grant-2025/journal.md +242 -1
  29. data/grant-2025/tasks.md +136 -41
  30. data/lib/uringmachine/actor.rb +8 -0
  31. data/lib/uringmachine/dns_resolver.rb +1 -2
  32. data/lib/uringmachine/fiber_scheduler.rb +283 -110
  33. data/lib/uringmachine/version.rb +1 -1
  34. data/lib/uringmachine.rb +32 -3
  35. data/test/helper.rb +7 -18
  36. data/test/test_actor.rb +12 -3
  37. data/test/test_async_op.rb +10 -10
  38. data/test/test_fiber.rb +84 -1
  39. data/test/test_fiber_scheduler.rb +1425 -20
  40. data/test/test_um.rb +565 -113
  41. data/uringmachine.gemspec +6 -5
  42. data/vendor/liburing/src/include/liburing/io_uring.h +1 -0
  43. data/vendor/liburing/src/include/liburing.h +13 -0
  44. data/vendor/liburing/src/liburing-ffi.map +1 -0
  45. data/vendor/liburing/test/bind-listen.c +175 -13
  46. data/vendor/liburing/test/read-write.c +4 -4
  47. data/vendor/liburing/test/ringbuf-read.c +4 -4
  48. data/vendor/liburing/test/send_recv.c +8 -7
  49. metadata +50 -28
  50. data/examples/bm_fileno.rb +0 -33
  51. data/examples/bm_queue.rb +0 -110
  52. data/examples/bm_side_running.rb +0 -83
  53. data/examples/bm_sqlite.rb +0 -89
  54. data/examples/dns_client.rb +0 -12
  55. /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
  56. /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
  57. /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
  58. /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
data/test/test_um.rb CHANGED
@@ -11,6 +11,82 @@ class UringMachineTest < Minitest::Test
11
11
  end
12
12
  end
13
13
 
14
+ class SizeTest < Minitest::Test
15
+ def test_default_size
16
+ m = UM.new
17
+ assert_equal 4096, m.size
18
+ end
19
+
20
+ def test_custom_size_value
21
+ m = UM.new(13)
22
+ assert_equal 13, m.size
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
+ assert_equal 0, machine.pending_fibers.size
77
+ machine.write_async(w, 'foo')
78
+ machine.write_async(w, 'bar')
79
+ machine.write_async(w, 'baz')
80
+ assert_equal 0, machine.pending_fibers.size
81
+
82
+ assert_equal 3, machine.metrics[:ops_pending]
83
+ assert_equal 3, machine.submit
84
+ assert_equal 0, machine.submit
85
+ machine.snooze
86
+ assert_equal 0, machine.metrics[:ops_pending]
87
+ end
88
+ end
89
+
14
90
  class SpinTest < UMBaseTest
15
91
  def test_spin
16
92
  x = nil
@@ -35,7 +111,7 @@ end
35
111
 
36
112
  class SnoozeTest < UMBaseTest
37
113
  def test_snooze_while_sleeping_fiber
38
- machine.spin do
114
+ f = machine.spin do
39
115
  machine.sleep(0.1)
40
116
  end
41
117
 
@@ -48,6 +124,9 @@ class SnoozeTest < UMBaseTest
48
124
  machine.snooze
49
125
  t1 = monotonic_clock
50
126
  assert_in_range 0..0.001, t1 - t0
127
+ ensure
128
+ machine.schedule(f, nil)
129
+ machine.join(f)
51
130
  end
52
131
  end
53
132
 
@@ -59,7 +138,7 @@ class ScheduleTest < UMBaseTest
59
138
  buf << [21, x]
60
139
  machine.schedule(main, 21)
61
140
  buf << 22
62
- x = machine.yield
141
+ x = machine.switch
63
142
  buf << [23, x]
64
143
  end
65
144
 
@@ -78,11 +157,10 @@ class ScheduleTest < UMBaseTest
78
157
  def test_schedule_exception
79
158
  buf = []
80
159
  f = Fiber.new do
81
- # this should raise
82
- machine.yield
160
+ machine.yield # this should raise
83
161
  rescue Exception => e
84
162
  buf << e
85
- machine.yield
163
+ machine.switch
86
164
  end
87
165
 
88
166
  machine.schedule(f, nil)
@@ -102,19 +180,19 @@ class ScheduleTest < UMBaseTest
102
180
  e = CustomError.new
103
181
  f = Fiber.new do
104
182
  machine.schedule(main, e)
105
- machine.yield
183
+ machine.switch
106
184
  end
107
185
 
108
186
  machine.schedule(f, nil)
109
187
  t0 = monotonic_clock
110
188
 
111
189
  # the call to schedule means an op is checked out
112
- assert_equal 0, machine.pending_count
190
+ assert_equal 0, machine.metrics[:ops_pending]
113
191
  begin
114
192
  machine.sleep(1)
115
193
  rescue Exception => e2
116
194
  end
117
- assert_equal 0, machine.pending_count
195
+ assert_equal 0, machine.metrics[:ops_pending]
118
196
  t1 = monotonic_clock
119
197
 
120
198
  assert_equal e2, e
@@ -137,7 +215,7 @@ class ScheduleTest < UMBaseTest
137
215
  buf << 5
138
216
  end
139
217
 
140
- assert_equal 0, machine.pending_count
218
+ assert_equal 0, machine.metrics[:ops_pending]
141
219
  assert_equal [1, 2, 5], buf
142
220
  assert_kind_of TOError, e
143
221
  end
@@ -176,9 +254,9 @@ class ScheduleTest < UMBaseTest
176
254
  rescue => e
177
255
  end
178
256
 
179
- assert_equal 1, machine.pending_count
257
+ assert_equal 1, machine.metrics[:ops_pending]
180
258
  machine.sleep(0.01) # wait for cancelled CQEs
181
- assert_equal 0, machine.pending_count
259
+ assert_equal 0, machine.metrics[:ops_pending]
182
260
 
183
261
  assert_kind_of RuntimeError, e
184
262
  assert_equal 'hi', e.message
@@ -189,9 +267,9 @@ class ScheduleTest < UMBaseTest
189
267
 
190
268
  assert_equal 42, v
191
269
 
192
- assert_equal 1, machine.pending_count
270
+ assert_equal 1, machine.metrics[:ops_pending]
193
271
  machine.sleep 0.01 # wait for cancelled CQE
194
- assert_equal 0, machine.pending_count
272
+ assert_equal 0, machine.metrics[:ops_pending]
195
273
  end
196
274
 
197
275
  def test_timeout_with_no_timeout
@@ -200,9 +278,9 @@ class ScheduleTest < UMBaseTest
200
278
 
201
279
  assert_equal 3, v
202
280
 
203
- assert_equal 1, machine.pending_count
281
+ assert_equal 1, machine.metrics[:ops_pending]
204
282
  machine.sleep 0.01 # wait for cancelled CQE
205
- assert_equal 0, machine.pending_count
283
+ assert_equal 0, machine.metrics[:ops_pending]
206
284
  end
207
285
 
208
286
  class TO2Error < RuntimeError; end
@@ -215,7 +293,7 @@ class ScheduleTest < UMBaseTest
215
293
  machine.timeout(0.04, TOError) do
216
294
  machine.timeout(0.02, TO2Error) do
217
295
  machine.timeout(0.03, TO3Error) do
218
- buf << machine.pending_count
296
+ buf << machine.metrics[:ops_pending]
219
297
  machine.sleep(1)
220
298
  end
221
299
  end
@@ -223,9 +301,9 @@ class ScheduleTest < UMBaseTest
223
301
  rescue => e
224
302
  end
225
303
 
226
- assert_equal 2, machine.pending_count
304
+ assert_equal 2, machine.metrics[:ops_pending]
227
305
  machine.sleep(0.01) # wait for cancelled CQEs
228
- assert_equal 0, machine.pending_count
306
+ assert_equal 0, machine.metrics[:ops_pending]
229
307
 
230
308
  assert_kind_of TO2Error, e
231
309
  assert_equal [3], buf
@@ -235,9 +313,9 @@ end
235
313
  class SleepTest < UMBaseTest
236
314
  def test_sleep
237
315
  t0 = monotonic_clock
238
- assert_equal 0, machine.pending_count
316
+ assert_equal 0, machine.metrics[:ops_pending]
239
317
  res = machine.sleep(0.1)
240
- assert_equal 0, machine.pending_count
318
+ assert_equal 0, machine.metrics[:ops_pending]
241
319
  t1 = monotonic_clock
242
320
  assert_in_range 0.09..0.13, t1 - t0
243
321
  assert_equal 0.1, res
@@ -294,7 +372,7 @@ class PeriodicallyTest < UMBaseTest
294
372
  cancel = 0
295
373
 
296
374
  t0 = monotonic_clock
297
- assert_equal 0, machine.pending_count
375
+ assert_equal 0, machine.metrics[:ops_pending]
298
376
  begin
299
377
  machine.periodically(0.01) do
300
378
  count += 1
@@ -304,7 +382,7 @@ class PeriodicallyTest < UMBaseTest
304
382
  cancel = 1
305
383
  end
306
384
  machine.snooze
307
- assert_equal 0, machine.pending_count
385
+ assert_equal 0, machine.metrics[:ops_pending]
308
386
  t1 = monotonic_clock
309
387
  assert_in_range 0.05..0.09, t1 - t0
310
388
  assert_equal 5, count
@@ -316,7 +394,7 @@ class PeriodicallyTest < UMBaseTest
316
394
  cancel = 0
317
395
 
318
396
  t0 = monotonic_clock
319
- assert_equal 0, machine.pending_count
397
+ assert_equal 0, machine.metrics[:ops_pending]
320
398
  begin
321
399
  machine.timeout(0.05, Cancel) do
322
400
  machine.periodically(0.01) do
@@ -327,25 +405,45 @@ class PeriodicallyTest < UMBaseTest
327
405
  rescue Cancel
328
406
  cancel = 1
329
407
  end
330
- 20.times { machine.snooze }
331
- assert_equal 0, machine.pending_count
408
+ assert_equal 1, cancel
332
409
  t1 = monotonic_clock
333
410
  assert_in_range 0.05..0.08, t1 - t0
334
411
  assert_in_range 4..6, count
335
- assert_equal 1, cancel
336
412
 
337
413
  end
338
414
  end
339
415
 
416
+ class StatsTest < UMBaseTest
417
+ def test_op_counts
418
+ _r, w = IO.pipe
419
+
420
+ assert_equal 0, machine.metrics[:ops_pending]
421
+ assert_equal 0, machine.metrics[:total_ops]
422
+ machine.write_async(w.fileno, 'foo')
423
+ assert_equal 1, machine.metrics[:ops_pending]
424
+ assert_equal 1, machine.metrics[:total_ops]
425
+ machine.snooze
426
+ assert_equal 0, machine.metrics[:ops_pending]
427
+ assert_equal 1, machine.metrics[:total_ops]
428
+
429
+ machine.write_async(w.fileno, 'foo')
430
+ assert_equal 1, machine.metrics[:ops_pending]
431
+ assert_equal 2, machine.metrics[:total_ops]
432
+ machine.snooze
433
+ assert_equal 0, machine.metrics[:ops_pending]
434
+ assert_equal 2, machine.metrics[:total_ops]
435
+ end
436
+ end
437
+
340
438
  class ReadTest < UMBaseTest
341
439
  def test_read
342
440
  r, w = IO.pipe
343
441
  w << 'foobar'
344
442
 
345
443
  buf = +''
346
- assert_equal 0, machine.pending_count
444
+ assert_equal 0, machine.metrics[:ops_pending]
347
445
  res = machine.read(r.fileno, buf, 3)
348
- assert_equal 0, machine.pending_count
446
+ assert_equal 0, machine.metrics[:ops_pending]
349
447
  assert_equal 3, res
350
448
  assert_equal 'foo', buf
351
449
 
@@ -367,7 +465,7 @@ class ReadTest < UMBaseTest
367
465
  assert_raises(Errno::EBADF) do
368
466
  machine.read(w.fileno, +'', 8192)
369
467
  end
370
- assert_equal 0, machine.pending_count
468
+ assert_equal 0, machine.metrics[:ops_pending]
371
469
  end
372
470
 
373
471
  def test_read_with_buffer_offset
@@ -464,6 +562,20 @@ class ReadTest < UMBaseTest
464
562
  machine.read(r, [])
465
563
  }
466
564
  end
565
+
566
+ def test_read_with_file_offset
567
+ fn = "/tmp/um_#{SecureRandom.hex}"
568
+ IO.write(fn, 'foobar')
569
+
570
+ fd = machine.open(fn, UM::O_RDONLY)
571
+ buffer = +''
572
+ result = machine.read(fd, buffer, 100, 0, 2)
573
+ assert_equal 4, result
574
+ assert_equal 'obar', buffer
575
+ ensure
576
+ machine.close(fd)
577
+ FileUtils.rm(fn) rescue nil
578
+ end
467
579
  end
468
580
 
469
581
  class ReadEachTest < UMBaseTest
@@ -483,7 +595,7 @@ class ReadEachTest < UMBaseTest
483
595
  w << 'baz'
484
596
  machine.sleep 0.02
485
597
  w.close
486
- machine.yield
598
+ machine.switch
487
599
  end
488
600
 
489
601
  machine.schedule(f, nil)
@@ -493,7 +605,7 @@ class ReadEachTest < UMBaseTest
493
605
  end
494
606
 
495
607
  assert_equal ['foo', 'bar', 'baz'], bufs
496
- assert_equal 0, machine.pending_count
608
+ assert_equal 0, machine.metrics[:ops_pending]
497
609
  end
498
610
 
499
611
  # send once and close write fd
@@ -517,7 +629,7 @@ class ReadEachTest < UMBaseTest
517
629
 
518
630
  assert_kind_of RuntimeError, e
519
631
  assert_equal 'hi', e.message
520
- assert_equal 0, machine.pending_count
632
+ assert_equal 0, machine.metrics[:ops_pending]
521
633
  end
522
634
 
523
635
  # send once and leave write fd open
@@ -542,7 +654,7 @@ class ReadEachTest < UMBaseTest
542
654
  assert_equal 'hi', e.message
543
655
 
544
656
  machine.snooze # in case the final CQE has not yet arrived
545
- assert_equal 0, machine.pending_count
657
+ assert_equal 0, machine.metrics[:ops_pending]
546
658
  end
547
659
 
548
660
  # send twice
@@ -568,7 +680,7 @@ class ReadEachTest < UMBaseTest
568
680
  assert_equal 'hi', e.message
569
681
 
570
682
  machine.snooze # in case the final CQE has not yet arrived
571
- assert_equal 0, machine.pending_count
683
+ assert_equal 0, machine.metrics[:ops_pending]
572
684
  end
573
685
 
574
686
  def test_read_each_break
@@ -592,7 +704,7 @@ class ReadEachTest < UMBaseTest
592
704
 
593
705
  assert_equal ['foo'], bufs
594
706
  machine.snooze # in case the final CQE has not yet arrived
595
- assert_equal 0, machine.pending_count
707
+ assert_equal 0, machine.metrics[:ops_pending]
596
708
  ensure
597
709
  t&.kill
598
710
  end
@@ -613,23 +725,43 @@ class WriteTest < UMBaseTest
613
725
  def test_write
614
726
  r, w = IO.pipe
615
727
 
616
- assert_equal 0, machine.pending_count
617
- machine.write(w.fileno, 'foo')
618
- assert_equal 0, machine.pending_count
728
+ assert_equal 0, machine.metrics[:ops_pending]
729
+ res = machine.write(w.fileno, 'foo')
730
+ assert_equal 3, res
731
+ assert_equal 0, machine.metrics[:ops_pending]
619
732
  assert_equal 'foo', r.readpartial(3)
620
733
 
621
- machine.write(w.fileno, 'bar', 2)
734
+ res = machine.write(w.fileno, 'bar', 2)
735
+ assert_equal 2, res
622
736
  assert_equal 'ba', r.readpartial(3)
623
737
  end
624
738
 
625
739
  def test_write_bad_fd
626
740
  r, _w = IO.pipe
627
741
 
628
- assert_equal 0, machine.pending_count
742
+ assert_equal 0, machine.metrics[:ops_pending]
629
743
  assert_raises(Errno::EBADF) do
630
744
  machine.write(r.fileno, 'foo')
631
745
  end
632
- assert_equal 0, machine.pending_count
746
+ assert_equal 0, machine.metrics[:ops_pending]
747
+ end
748
+
749
+ def test_write_zero_length
750
+ r, w = IO.pipe
751
+
752
+ res = machine.write(w.fileno, '')
753
+ assert_equal 0, res
754
+
755
+ res = machine.write(w.fileno, 'bar', 0)
756
+ assert_equal 0, res
757
+
758
+ buf = IO::Buffer.new(3)
759
+ buf.set_string('baz')
760
+ res = machine.write(w.fileno, buf, 0, 0)
761
+ assert_equal 0, res
762
+
763
+ w.close
764
+ assert_equal '', r.read
633
765
  end
634
766
 
635
767
  def test_write_io_buffer
@@ -679,45 +811,58 @@ class WriteTest < UMBaseTest
679
811
  machine.write(w, [])
680
812
  }
681
813
  end
814
+
815
+ def test_write_with_file_offset
816
+ fn = "/tmp/um_#{SecureRandom.hex}"
817
+ IO.write(fn, 'foobar')
818
+
819
+ fd = machine.open(fn, UM::O_WRONLY)
820
+ result = machine.write(fd, 'baz', -1, 2)
821
+ assert_equal 3, result
822
+ assert_equal 'fobazr', IO.read(fn)
823
+ ensure
824
+ machine.close(fd)
825
+ FileUtils.rm(fn) rescue nil
826
+ end
682
827
  end
683
828
 
684
829
  class WriteAsyncTest < UMBaseTest
685
830
  def test_write_async
686
831
  r, w = IO.pipe
687
832
 
688
- assert_equal 0, machine.pending_count
833
+ assert_equal 0, machine.metrics[:ops_pending]
689
834
  machine.write_async(w.fileno, 'foo')
690
- assert_equal 1, machine.pending_count
835
+ assert_equal 1, machine.metrics[:ops_pending]
691
836
 
692
- machine.snooze
693
- assert_equal 0, machine.pending_count
837
+ machine.snooze while machine.metrics[:ops_pending] > 0
838
+ assert_equal 0, machine.metrics[:ops_pending]
694
839
  assert_equal 'foo', r.readpartial(3)
695
840
  end
696
841
 
697
842
  def test_write_async_dynamic_string
698
843
  r, w = IO.pipe
699
844
 
700
- assert_equal 0, machine.pending_count
845
+ assert_equal 0, machine.metrics[:ops_pending]
701
846
  str = "foo#{123}#{'bar' * 48}"
702
847
  len = str.bytesize
703
848
  machine.write_async(w.fileno, str)
704
849
  str = nil
705
- GC.start
706
- assert_equal 1, machine.pending_count
850
+ # GC.start
851
+ assert_equal 1, machine.metrics[:ops_pending]
707
852
 
708
- machine.snooze
709
- assert_equal 0, machine.pending_count
853
+ machine.snooze while machine.metrics[:ops_pending] > 0
854
+ assert_equal 0, machine.metrics[:ops_pending]
710
855
  assert_equal "foo#{123}#{'bar' * 48}", r.readpartial(len)
711
856
  end
712
857
 
713
858
  def test_write_async_bad_fd
714
859
  r, _w = IO.pipe
715
860
 
716
- assert_equal 0, machine.pending_count
861
+ assert_equal 0, machine.metrics[:ops_pending]
717
862
  machine.write_async(r.fileno, 'foo')
718
- assert_equal 1, machine.pending_count
863
+ assert_equal 1, machine.metrics[:ops_pending]
719
864
  machine.snooze
720
- assert_equal 0, machine.pending_count
865
+ assert_equal 0, machine.metrics[:ops_pending]
721
866
  end
722
867
 
723
868
  def test_write_async_io_buffer
@@ -728,7 +873,7 @@ class WriteAsyncTest < UMBaseTest
728
873
  write_buffer.set_string(msg)
729
874
 
730
875
  machine.write_async(w, write_buffer)
731
- 3.times { machine.snooze }
876
+ machine.snooze while machine.metrics[:ops_pending] > 0
732
877
  machine.close(w)
733
878
 
734
879
  str = +''
@@ -741,6 +886,33 @@ class WriteAsyncTest < UMBaseTest
741
886
 
742
887
  assert_raises(UM::Error) { machine.write_async(w, []) }
743
888
  end
889
+
890
+ def test_write_async_with_len
891
+ r, w = IO.pipe
892
+
893
+ assert_equal 0, machine.metrics[:ops_pending]
894
+ machine.write_async(w.fileno, 'foobar', 4)
895
+
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]
899
+ assert_equal 'foob', r.readpartial(6)
900
+ end
901
+
902
+ def test_write_async_with_file_offset
903
+ fn = "/tmp/um_#{SecureRandom.hex}"
904
+ IO.write(fn, 'foobar')
905
+
906
+ fd = machine.open(fn, UM::O_WRONLY)
907
+ machine.write_async(fd, 'baz', -1, 2)
908
+
909
+ assert_equal 1, machine.metrics[:ops_pending]
910
+ machine.snooze while machine.metrics[:ops_pending] > 0
911
+ assert_equal 'fobazr', IO.read(fn)
912
+ ensure
913
+ machine.close(fd)
914
+ FileUtils.rm(fn) rescue nil
915
+ end
744
916
  end
745
917
 
746
918
  class CloseTest < UMBaseTest
@@ -749,9 +921,9 @@ class CloseTest < UMBaseTest
749
921
  machine.write(w.fileno, 'foo')
750
922
  assert_equal 'foo', r.readpartial(3)
751
923
 
752
- assert_equal 0, machine.pending_count
924
+ assert_equal 0, machine.metrics[:ops_pending]
753
925
  machine.close(w.fileno)
754
- assert_equal 0, machine.pending_count
926
+ assert_equal 0, machine.metrics[:ops_pending]
755
927
  assert_equal '', r.read
756
928
 
757
929
  assert_raises(Errno::EBADF) { machine.close(w.fileno) }
@@ -771,18 +943,18 @@ class CloseAsyncTest < UMBaseTest
771
943
  machine.write(w.fileno, 'foo')
772
944
  assert_equal 'foo', r.readpartial(3)
773
945
 
774
- assert_equal 0, machine.pending_count
775
- machine.close_async(w.fileno)
776
- 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]
777
949
  machine.snooze
778
- assert_equal 0, machine.pending_count
950
+ assert_equal 0, machine.metrics[:ops_pending]
779
951
  assert_equal '', r.read
780
952
  end
781
953
  end
782
954
 
783
955
  class ShutdownTest < UMBaseTest
784
956
  def test_shutdown
785
- c_fd, s_fd = make_socket_pair
957
+ c_fd, s_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
786
958
  res = @machine.send(c_fd, 'abc', 3, 0)
787
959
  assert_equal 3, res
788
960
 
@@ -814,7 +986,7 @@ end
814
986
 
815
987
  class ShutdownAsyncTest < UMBaseTest
816
988
  def test_shutdown_async
817
- c_fd, s_fd = make_socket_pair
989
+ c_fd, s_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
818
990
  res = @machine.send(c_fd, 'abc', 3, 0)
819
991
  assert_equal 3, res
820
992
 
@@ -858,9 +1030,9 @@ class AcceptTest < UMBaseTest
858
1030
  def test_accept
859
1031
  conn = TCPSocket.new('127.0.0.1', @port)
860
1032
 
861
- assert_equal 0, machine.pending_count
1033
+ assert_equal 0, machine.metrics[:ops_pending]
862
1034
  fd = machine.accept(@server.fileno)
863
- assert_equal 0, machine.pending_count
1035
+ assert_equal 0, machine.metrics[:ops_pending]
864
1036
  assert_kind_of Integer, fd
865
1037
  assert fd > 0
866
1038
 
@@ -897,7 +1069,7 @@ class AcceptEachTest < UMBaseTest
897
1069
  end
898
1070
 
899
1071
  assert_equal 3, count
900
- assert_equal 0, machine.pending_count
1072
+ assert_equal 0, machine.metrics[:ops_pending]
901
1073
  ensure
902
1074
  t&.kill
903
1075
  end
@@ -932,9 +1104,9 @@ end
932
1104
 
933
1105
  class SocketTest < UMBaseTest
934
1106
  def test_socket
935
- assert_equal 0, machine.pending_count
1107
+ assert_equal 0, machine.metrics[:ops_pending]
936
1108
  fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0);
937
- assert_equal 0, machine.pending_count
1109
+ assert_equal 0, machine.metrics[:ops_pending]
938
1110
  assert_kind_of Integer, fd
939
1111
  assert fd > 0
940
1112
 
@@ -962,9 +1134,9 @@ class ConnectTest < UMBaseTest
962
1134
  end
963
1135
 
964
1136
  fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
965
- assert_equal 0, machine.pending_count
1137
+ assert_equal 0, machine.metrics[:ops_pending]
966
1138
  res = machine.connect(fd, '127.0.0.1', @port)
967
- assert_equal 0, machine.pending_count
1139
+ assert_equal 0, machine.metrics[:ops_pending]
968
1140
  assert_equal 0, res
969
1141
 
970
1142
  buf = +''
@@ -977,9 +1149,9 @@ class ConnectTest < UMBaseTest
977
1149
 
978
1150
  def test_connect_with_bad_addr
979
1151
  fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0);
980
- assert_equal 0, machine.pending_count
1152
+ assert_equal 0, machine.metrics[:ops_pending]
981
1153
  assert_raises(Errno::ENETUNREACH) { machine.connect(fd, 'a.b.c.d', @port) }
982
- assert_equal 0, machine.pending_count
1154
+ assert_equal 0, machine.metrics[:ops_pending]
983
1155
  end
984
1156
  end
985
1157
 
@@ -1195,11 +1367,11 @@ class BindTest < UMBaseTest
1195
1367
  end
1196
1368
 
1197
1369
  def test_bind
1198
- assert_equal 0, machine.pending_count
1370
+ assert_equal 0, machine.metrics[:ops_pending]
1199
1371
  fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0)
1200
1372
  res = machine.bind(fd, '127.0.0.1', @port)
1201
1373
  assert_equal 0, res
1202
- assert_equal 0, machine.pending_count
1374
+ assert_equal 0, machine.metrics[:ops_pending]
1203
1375
 
1204
1376
  peer = UDPSocket.new
1205
1377
  peer.connect('127.0.0.1', @port)
@@ -1212,13 +1384,13 @@ class BindTest < UMBaseTest
1212
1384
  end
1213
1385
 
1214
1386
  def test_bind_invalid_args
1215
- assert_equal 0, machine.pending_count
1387
+ assert_equal 0, machine.metrics[:ops_pending]
1216
1388
 
1217
1389
  fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0)
1218
1390
  assert_raises(Errno::EACCES) { machine.bind(fd, 'foo.bar.baz', 3) }
1219
1391
  assert_raises(Errno::EBADF) { machine.bind(-3, '127.0.01', 1234) }
1220
1392
 
1221
- assert_equal 0, machine.pending_count
1393
+ assert_equal 0, machine.metrics[:ops_pending]
1222
1394
  end
1223
1395
  end
1224
1396
 
@@ -1233,7 +1405,7 @@ class ListenTest < UMBaseTest
1233
1405
  machine.bind(fd, '127.0.0.1', @port)
1234
1406
  res = machine.listen(fd, 5)
1235
1407
  assert_equal 0, res
1236
- assert_equal 0, machine.pending_count
1408
+ assert_equal 0, machine.metrics[:ops_pending]
1237
1409
 
1238
1410
  conn = nil
1239
1411
  t = Thread.new do
@@ -1276,8 +1448,6 @@ end
1276
1448
 
1277
1449
  class SynchronizeTest < UMBaseTest
1278
1450
  def test_synchronize_single
1279
- skip if !machine.respond_to?(:synchronize)
1280
-
1281
1451
  m = UM::Mutex.new
1282
1452
 
1283
1453
  buf = []
@@ -1289,13 +1459,11 @@ class SynchronizeTest < UMBaseTest
1289
1459
  end
1290
1460
 
1291
1461
  assert_equal [1, 2], buf
1292
- assert_equal 0, machine.pending_count
1462
+ assert_equal 0, machine.metrics[:ops_pending]
1293
1463
  end
1294
1464
 
1295
1465
  def test_synchronize_pair
1296
- skip if !machine.respond_to?(:synchronize)
1297
1466
  m = UM::Mutex.new
1298
-
1299
1467
  buf = []
1300
1468
 
1301
1469
  f1 = Fiber.new do
@@ -1305,7 +1473,7 @@ class SynchronizeTest < UMBaseTest
1305
1473
  buf << 12
1306
1474
  end
1307
1475
  buf << 13
1308
- machine.yield
1476
+ machine.switch
1309
1477
  end
1310
1478
 
1311
1479
  f2 = Fiber.new do
@@ -1315,7 +1483,7 @@ class SynchronizeTest < UMBaseTest
1315
1483
  buf << 22
1316
1484
  end
1317
1485
  buf << 23
1318
- machine.yield
1486
+ machine.switch
1319
1487
  end
1320
1488
 
1321
1489
  machine.schedule(f1, nil)
@@ -1323,7 +1491,7 @@ class SynchronizeTest < UMBaseTest
1323
1491
 
1324
1492
  machine.sleep(0.03)
1325
1493
  assert_equal [11, 12, 13, 21, 22, 23], buf
1326
- assert_equal 0, machine.pending_count
1494
+ assert_equal 0, machine.metrics[:ops_pending]
1327
1495
  end
1328
1496
 
1329
1497
  def test_synchronize_multi
@@ -1348,8 +1516,6 @@ end
1348
1516
 
1349
1517
  class QueueTest < UMBaseTest
1350
1518
  def test_push_pop_1
1351
- skip if !machine.respond_to?(:synchronize)
1352
-
1353
1519
  q = UM::Queue.new
1354
1520
  assert_equal 0, q.count
1355
1521
  machine.push(q, :foo)
@@ -1363,8 +1529,6 @@ class QueueTest < UMBaseTest
1363
1529
  end
1364
1530
 
1365
1531
  def test_push_pop_2
1366
- skip if !machine.respond_to?(:synchronize)
1367
-
1368
1532
  q = UM::Queue.new
1369
1533
  buf = []
1370
1534
 
@@ -1378,12 +1542,12 @@ class QueueTest < UMBaseTest
1378
1542
 
1379
1543
  machine.snooze
1380
1544
  assert_equal [], buf
1381
- assert_equal 2, machine.pending_count
1545
+ assert_equal 2, machine.metrics[:ops_pending]
1382
1546
 
1383
1547
  machine.push(q, :foo)
1384
1548
  assert_equal 1, q.count
1385
1549
  machine.snooze
1386
- assert_equal 1, machine.pending_count
1550
+ assert_equal 1, machine.metrics[:ops_pending]
1387
1551
  assert_equal [[1, :foo]], buf
1388
1552
 
1389
1553
  machine.push(q, :bar)
@@ -1395,8 +1559,6 @@ class QueueTest < UMBaseTest
1395
1559
  end
1396
1560
 
1397
1561
  def test_push_pop_3
1398
- skip if !machine.respond_to?(:synchronize)
1399
-
1400
1562
  q = UM::Queue.new
1401
1563
  buf = []
1402
1564
 
@@ -1406,13 +1568,13 @@ class QueueTest < UMBaseTest
1406
1568
 
1407
1569
  f1 = Fiber.new do
1408
1570
  buf << [1, machine.pop(q)]
1409
- machine.yield
1571
+ machine.switch
1410
1572
  end
1411
1573
  machine.schedule(f1, nil)
1412
1574
 
1413
1575
  f2 = Fiber.new do
1414
1576
  buf << [2, machine.pop(q)]
1415
- machine.yield
1577
+ machine.switch
1416
1578
  end
1417
1579
  machine.schedule(f2, nil)
1418
1580
 
@@ -1423,8 +1585,6 @@ class QueueTest < UMBaseTest
1423
1585
  end
1424
1586
 
1425
1587
  def test_push_pop_4
1426
- skip if !machine.respond_to?(:synchronize)
1427
-
1428
1588
  q = UM::Queue.new
1429
1589
  buf = []
1430
1590
 
@@ -1433,13 +1593,13 @@ class QueueTest < UMBaseTest
1433
1593
 
1434
1594
  f1 = Fiber.new do
1435
1595
  buf << [1, machine.pop(q)]
1436
- machine.yield
1596
+ machine.switch
1437
1597
  end
1438
1598
  machine.schedule(f1, nil)
1439
1599
 
1440
1600
  f2 = Fiber.new do
1441
1601
  buf << [2, machine.pop(q)]
1442
- machine.yield
1602
+ machine.switch
1443
1603
  end
1444
1604
  machine.schedule(f2, nil)
1445
1605
 
@@ -1452,8 +1612,6 @@ class QueueTest < UMBaseTest
1452
1612
  end
1453
1613
 
1454
1614
  def test_push_shift_1
1455
- skip if !machine.respond_to?(:synchronize)
1456
-
1457
1615
  q = UM::Queue.new
1458
1616
 
1459
1617
  machine.push(q, :foo)
@@ -1466,8 +1624,6 @@ class QueueTest < UMBaseTest
1466
1624
  end
1467
1625
 
1468
1626
  def test_shift_shift_1
1469
- skip if !machine.respond_to?(:synchronize)
1470
-
1471
1627
  q = UM::Queue.new
1472
1628
 
1473
1629
  machine.unshift(q, :foo)
@@ -1479,6 +1635,15 @@ class QueueTest < UMBaseTest
1479
1635
  assert_equal :foo, machine.shift(q)
1480
1636
  end
1481
1637
 
1638
+ def test_shift_exception_value
1639
+ q = UM::Queue.new
1640
+ machine.unshift(q, Exception.new("foo"))
1641
+
1642
+ e = machine.shift(q)
1643
+ assert_kind_of Exception, e
1644
+ assert_equal "foo", e.message
1645
+ end
1646
+
1482
1647
  def test_cross_thread_push_shift
1483
1648
  q = UM::Queue.new
1484
1649
 
@@ -1502,41 +1667,79 @@ class QueueTest < UMBaseTest
1502
1667
 
1503
1668
  assert_equal [0, 1, 2], items
1504
1669
  end
1670
+
1671
+ def test_cross_thread_cross_queue_comms
1672
+ worker_queue = UM::Queue.new
1673
+
1674
+ buf = []
1675
+ t1 = Thread.new {
1676
+ m = UM.new
1677
+ (1..10).each {
1678
+ q = UM::Queue.new
1679
+ m.push(worker_queue, [q, it])
1680
+ res = m.shift(q)
1681
+ buf << res
1682
+ }
1683
+ }
1684
+
1685
+ t2 = Thread.new {
1686
+ m = UM.new
1687
+ loop do
1688
+ q, v = m.shift(worker_queue)
1689
+ break if q == :STOP
1690
+
1691
+ res = v * 10
1692
+ m.push(q, res)
1693
+ end
1694
+ }
1695
+
1696
+ t1.join
1697
+ q = UM::Queue.new
1698
+ machine.push(worker_queue, :STOP)
1699
+ t2.join
1700
+
1701
+ assert_equal (1..10).map { it * 10 }, buf
1702
+ ensure
1703
+ t1.kill rescue nil
1704
+ t2.kill rescue nil
1705
+ end
1505
1706
  end
1506
1707
 
1507
1708
  class OpenTest < UMBaseTest
1508
- PATH = '/tmp/um_open_test'
1509
-
1510
1709
  def setup
1511
1710
  super
1512
- FileUtils.rm(PATH, force: true)
1711
+ @fn = "/tmp/um_#{SecureRandom.hex}"
1712
+ end
1713
+
1714
+ def teardown
1715
+ FileUtils.rm(@fn) rescue nil
1513
1716
  end
1514
1717
 
1515
1718
  def test_open
1516
- fd = machine.open(PATH, UM::O_CREAT | UM::O_WRONLY)
1719
+ fd = machine.open(@fn, UM::O_CREAT | UM::O_WRONLY)
1517
1720
  assert_kind_of Integer, fd
1518
- assert File.file?(PATH)
1721
+ assert File.file?(@fn)
1519
1722
 
1520
1723
  machine.write(fd, 'foo')
1521
1724
  machine.close(fd)
1522
1725
 
1523
- assert_equal 'foo', IO.read(PATH)
1726
+ assert_equal 'foo', IO.read(@fn)
1524
1727
  end
1525
1728
 
1526
1729
  def test_open_with_block
1527
- 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|
1528
1731
  machine.write(fd, 'bar')
1529
1732
  fd
1530
1733
  end
1531
1734
 
1532
1735
  assert_kind_of Integer, res
1533
1736
  assert_raises(Errno::EBADF) { machine.close(res) }
1534
- assert_equal 'bar', IO.read(PATH)
1737
+ assert_equal 'bar', IO.read(@fn)
1535
1738
  end
1536
1739
 
1537
1740
  def test_open_bad_arg
1538
- assert_raises(Errno::ENOENT) { machine.open(PATH, UM::O_RDONLY) }
1539
- 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) {} }
1540
1743
  end
1541
1744
  end
1542
1745
 
@@ -1560,6 +1763,26 @@ class PipeTest < UMBaseTest
1560
1763
  end
1561
1764
  end
1562
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
+
1563
1786
  class PidfdTest < UMBaseTest
1564
1787
  def test_pidfd_open
1565
1788
  pid = fork { exit 13 }
@@ -1646,6 +1869,85 @@ class PollTest < UMBaseTest
1646
1869
  end
1647
1870
  end
1648
1871
 
1872
+ class SelectTest < UMBaseTest
1873
+ def test_select
1874
+ rfd1, wfd1 = UM.pipe
1875
+ rfd2, wfd2 = UM.pipe
1876
+
1877
+ events = []
1878
+ machine.spin do
1879
+ events << 1
1880
+ events << machine.select([rfd1, rfd2], [], [])
1881
+ events << 2
1882
+ events << machine.select([rfd1, rfd2], [], [])
1883
+ events << 3
1884
+ machine.snooze
1885
+ events << machine.select([], [wfd1, wfd2], [])
1886
+ events << 4
1887
+ end
1888
+
1889
+ machine.snooze
1890
+ assert_equal [1], events
1891
+
1892
+ machine.write(wfd1, 'foo')
1893
+ machine.snooze
1894
+ assert_equal [1, [[rfd1], [], []], 2], events
1895
+
1896
+ machine.write(wfd2, 'foo')
1897
+
1898
+ machine.snooze
1899
+ assert_equal [1, [[rfd1], [], []], 2, [[rfd1, rfd2], [], []], 3], events
1900
+
1901
+ machine.snooze
1902
+
1903
+ assert_equal [
1904
+ 1, [[rfd1], [], []],
1905
+ 2, [[rfd1, rfd2], [], []],
1906
+ 3, [[], [wfd1, wfd2], []],
1907
+ 4
1908
+ ], events
1909
+
1910
+ machine.close(rfd1)
1911
+ machine.close(rfd2)
1912
+ end
1913
+
1914
+ def test_select_single
1915
+ rfd1, wfd1 = UM.pipe
1916
+
1917
+ events = []
1918
+ machine.spin do
1919
+ events << 1
1920
+ events << machine.select([rfd1], [], [])
1921
+ events << 2
1922
+ machine.snooze
1923
+ events << machine.select([], [wfd1], [])
1924
+ events << 3
1925
+ end
1926
+
1927
+ machine.snooze
1928
+ assert_equal [1], events
1929
+
1930
+ machine.write(wfd1, 'foo')
1931
+ machine.snooze
1932
+ assert_equal [1, [[rfd1], [], []], 2], events
1933
+
1934
+ 3.times { machine.snooze }
1935
+ assert_equal [1, [[rfd1], [], []], 2, [[], [wfd1], []], 3], events
1936
+
1937
+ machine.close(rfd1)
1938
+ machine.close(wfd1)
1939
+ end
1940
+
1941
+ def test_select_empty
1942
+ ret = machine.select([], [], [])
1943
+ assert_equal [[], [], []], ret
1944
+ end
1945
+
1946
+ def test_select_bad_fd
1947
+ assert_raises(Errno::EBADF) { machine.select([9876, 9877], [], []) }
1948
+ end
1949
+ end
1950
+
1649
1951
  class WaitidTest < UMBaseTest
1650
1952
  def test_waitid
1651
1953
  msg = 'hello from child'
@@ -1892,7 +2194,7 @@ end
1892
2194
  class SendBundleTest < UMBaseTest
1893
2195
  def setup
1894
2196
  super
1895
- @client_fd, @server_fd = make_socket_pair
2197
+ @client_fd, @server_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
1896
2198
  end
1897
2199
 
1898
2200
  def test_send_bundle_splat
@@ -1966,3 +2268,153 @@ class NonBlockTest < UMBaseTest
1966
2268
  assert_equal true, UM.io_nonblock?(r)
1967
2269
  end
1968
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