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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +14 -0
- data/TODO.md +144 -0
- data/benchmark/README.md +173 -0
- data/benchmark/bm_io_pipe.rb +70 -0
- data/benchmark/bm_io_socketpair.rb +71 -0
- data/benchmark/bm_mutex_cpu.rb +57 -0
- data/benchmark/bm_mutex_io.rb +64 -0
- data/benchmark/bm_pg_client.rb +109 -0
- data/benchmark/bm_queue.rb +76 -0
- data/benchmark/chart.png +0 -0
- data/benchmark/common.rb +135 -0
- data/benchmark/dns_client.rb +47 -0
- data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
- data/benchmark/run_bm.rb +8 -0
- data/benchmark/sqlite.rb +108 -0
- data/{examples/bm_write.rb → benchmark/write.rb} +4 -4
- data/ext/um/um.c +189 -100
- data/ext/um/um.h +36 -10
- data/ext/um/um_async_op.c +1 -1
- data/ext/um/um_class.c +87 -13
- data/ext/um/um_op.c +6 -0
- data/ext/um/um_sync.c +2 -2
- data/ext/um/um_utils.c +16 -0
- data/grant-2025/journal.md +118 -1
- data/grant-2025/tasks.md +48 -22
- data/lib/uringmachine/actor.rb +8 -0
- data/lib/uringmachine/dns_resolver.rb +1 -2
- data/lib/uringmachine/fiber_scheduler.rb +127 -81
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +32 -3
- data/test/helper.rb +7 -18
- data/test/test_actor.rb +12 -3
- data/test/test_async_op.rb +10 -10
- data/test/test_fiber.rb +84 -1
- data/test/test_fiber_scheduler.rb +950 -47
- data/test/test_um.rb +297 -120
- data/uringmachine.gemspec +2 -1
- metadata +38 -16
- data/examples/bm_fileno.rb +0 -33
- data/examples/bm_queue.rb +0 -111
- data/examples/bm_side_running.rb +0 -83
- data/examples/bm_sqlite.rb +0 -89
- data/examples/dns_client.rb +0 -12
- /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
- /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
- /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
- /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
|
@@ -4,6 +4,8 @@ require_relative 'helper'
|
|
|
4
4
|
require 'uringmachine/fiber_scheduler'
|
|
5
5
|
require 'securerandom'
|
|
6
6
|
require 'socket'
|
|
7
|
+
require 'net/http'
|
|
8
|
+
require 'json'
|
|
7
9
|
|
|
8
10
|
class MethodCallAuditor
|
|
9
11
|
attr_reader :calls
|
|
@@ -19,7 +21,7 @@ class MethodCallAuditor
|
|
|
19
21
|
res = @target.send(sym, *args, &block)
|
|
20
22
|
@calls << ({ sym:, args:, res:})
|
|
21
23
|
res
|
|
22
|
-
rescue => e
|
|
24
|
+
rescue Exception => e
|
|
23
25
|
@calls << ({ sym:, args:, res: e})
|
|
24
26
|
raise
|
|
25
27
|
end
|
|
@@ -39,7 +41,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
39
41
|
|
|
40
42
|
def teardown
|
|
41
43
|
Fiber.set_scheduler(nil)
|
|
42
|
-
|
|
44
|
+
super
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
def test_fiber_scheduler_initialize_without_machine
|
|
@@ -101,7 +103,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
101
103
|
kernel_sleep: 2,
|
|
102
104
|
io_write: 2,
|
|
103
105
|
io_read: 3,
|
|
104
|
-
|
|
106
|
+
io_close: 1,
|
|
105
107
|
join: 1
|
|
106
108
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
107
109
|
ensure
|
|
@@ -127,6 +129,9 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
127
129
|
io_read: 1,
|
|
128
130
|
join: 1
|
|
129
131
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
132
|
+
ensure
|
|
133
|
+
i.close rescue nil
|
|
134
|
+
o.close rescue nil
|
|
130
135
|
end
|
|
131
136
|
|
|
132
137
|
def test_io_write_with_timeout
|
|
@@ -149,10 +154,34 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
149
154
|
io_write: 1,
|
|
150
155
|
join: 1
|
|
151
156
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
157
|
+
ensure
|
|
158
|
+
i.close rescue nil
|
|
159
|
+
o.close rescue nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_io_write_ioerror
|
|
163
|
+
i, o = IO.pipe
|
|
164
|
+
buf = []
|
|
165
|
+
|
|
166
|
+
Fiber.schedule do
|
|
167
|
+
buf << i.write('!')
|
|
168
|
+
rescue SystemCallError, IOError => e
|
|
169
|
+
buf << e
|
|
170
|
+
end
|
|
171
|
+
@scheduler.join
|
|
172
|
+
assert_kind_of IOError, buf.first
|
|
173
|
+
|
|
174
|
+
assert_equal({
|
|
175
|
+
fiber: 1,
|
|
176
|
+
join: 1
|
|
177
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
178
|
+
ensure
|
|
179
|
+
i.close rescue nil
|
|
180
|
+
o.close rescue nil
|
|
152
181
|
end
|
|
153
182
|
|
|
154
183
|
def test_fiber_io_pread
|
|
155
|
-
fn = "/tmp
|
|
184
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
156
185
|
IO.write(fn, 'foobar')
|
|
157
186
|
|
|
158
187
|
buf = nil
|
|
@@ -170,12 +199,15 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
170
199
|
fiber: 1,
|
|
171
200
|
blocking_operation_wait: 1,
|
|
172
201
|
io_pread: 1,
|
|
202
|
+
io_close: 1,
|
|
173
203
|
join: 1
|
|
174
204
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
205
|
+
ensure
|
|
206
|
+
FileUtils.rm(fn) rescue nil
|
|
175
207
|
end
|
|
176
208
|
|
|
177
209
|
def test_fiber_scheduler_io_pwrite
|
|
178
|
-
fn = "/tmp
|
|
210
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
179
211
|
IO.write(fn, 'foobar')
|
|
180
212
|
|
|
181
213
|
res = nil
|
|
@@ -191,22 +223,25 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
191
223
|
assert_equal 'fobazr', IO.read(fn)
|
|
192
224
|
assert_equal({
|
|
193
225
|
fiber: 1,
|
|
194
|
-
blocking_operation_wait:
|
|
226
|
+
blocking_operation_wait: 1,
|
|
195
227
|
io_pwrite: 1,
|
|
228
|
+
io_close: 1,
|
|
196
229
|
join: 1
|
|
197
230
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
231
|
+
ensure
|
|
232
|
+
FileUtils.rm(fn) rescue nil
|
|
198
233
|
end
|
|
199
234
|
|
|
200
235
|
def test_fiber_scheduler_sleep
|
|
201
236
|
t0 = monotonic_clock
|
|
202
|
-
assert_equal 0, machine.
|
|
237
|
+
assert_equal 0, machine.metrics[:ops_pending]
|
|
203
238
|
Fiber.schedule do
|
|
204
239
|
sleep(0.01)
|
|
205
240
|
end
|
|
206
241
|
Fiber.schedule do
|
|
207
242
|
sleep(0.02)
|
|
208
243
|
end
|
|
209
|
-
assert_equal 2, machine.
|
|
244
|
+
assert_equal 2, machine.metrics[:ops_pending]
|
|
210
245
|
@scheduler.join
|
|
211
246
|
t1 = monotonic_clock
|
|
212
247
|
assert_in_range 0.02..0.025, t1 - t0
|
|
@@ -244,7 +279,8 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
244
279
|
end
|
|
245
280
|
|
|
246
281
|
def test_fiber_scheduler_process_wait
|
|
247
|
-
skip
|
|
282
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
283
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
248
284
|
|
|
249
285
|
child_pid = nil
|
|
250
286
|
status = nil
|
|
@@ -282,7 +318,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
282
318
|
buf = +''
|
|
283
319
|
sent = nil
|
|
284
320
|
|
|
285
|
-
assert_equal 0, machine.
|
|
321
|
+
assert_equal 0, machine.metrics[:total_ops]
|
|
286
322
|
Fiber.schedule do
|
|
287
323
|
buf = s1.recv(12)
|
|
288
324
|
end
|
|
@@ -292,7 +328,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
292
328
|
|
|
293
329
|
# In Ruby, sockets are by default non-blocking. The recv will cause io_wait
|
|
294
330
|
# to be invoked, the send should get through without needing to poll.
|
|
295
|
-
assert_equal 1, machine.
|
|
331
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
296
332
|
@scheduler.join
|
|
297
333
|
|
|
298
334
|
assert_equal 6, sent
|
|
@@ -308,48 +344,58 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
308
344
|
end
|
|
309
345
|
|
|
310
346
|
def test_fiber_scheduler_io_write_io_read
|
|
311
|
-
fn = "/tmp
|
|
347
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
312
348
|
Fiber.schedule do
|
|
313
349
|
IO.write(fn, 'foobar')
|
|
314
350
|
end
|
|
315
|
-
assert_equal 1, machine.
|
|
351
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
316
352
|
|
|
317
353
|
buf = nil
|
|
318
354
|
Fiber.schedule do
|
|
355
|
+
sleep 0.001
|
|
319
356
|
buf = IO.read(fn)
|
|
320
357
|
end
|
|
321
|
-
assert_equal 2, machine.
|
|
358
|
+
assert_equal 2, machine.metrics[:total_ops]
|
|
322
359
|
|
|
323
360
|
@scheduler.join
|
|
324
361
|
assert_equal 'foobar', buf
|
|
325
362
|
assert_equal({
|
|
326
363
|
fiber: 2,
|
|
327
|
-
blocking_operation_wait:
|
|
364
|
+
blocking_operation_wait: 2,
|
|
328
365
|
io_read: 2,
|
|
366
|
+
io_close: 2,
|
|
367
|
+
kernel_sleep: 1,
|
|
329
368
|
join: 1
|
|
330
369
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
370
|
+
ensure
|
|
371
|
+
FileUtils.rm(fn) rescue nil
|
|
331
372
|
end
|
|
332
373
|
|
|
333
374
|
def test_fiber_scheduler_file_io
|
|
334
|
-
fn = "/tmp
|
|
375
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
335
376
|
Fiber.schedule do
|
|
336
377
|
File.open(fn, 'w') { it.write 'foobar' }
|
|
337
378
|
end
|
|
338
|
-
assert_equal 1, machine.
|
|
379
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
339
380
|
|
|
340
381
|
buf = nil
|
|
341
382
|
Fiber.schedule do
|
|
383
|
+
sleep 0.001
|
|
342
384
|
File.open(fn, 'r') { buf = it.read }
|
|
343
385
|
end
|
|
344
|
-
assert_equal 2, machine.
|
|
386
|
+
assert_equal 2, machine.metrics[:total_ops]
|
|
345
387
|
@scheduler.join
|
|
346
388
|
assert_equal 'foobar', buf
|
|
347
389
|
assert_equal({
|
|
348
390
|
fiber: 2,
|
|
349
|
-
blocking_operation_wait:
|
|
391
|
+
blocking_operation_wait: 2,
|
|
350
392
|
io_read: 2,
|
|
393
|
+
io_close: 2,
|
|
394
|
+
kernel_sleep: 1,
|
|
351
395
|
join: 1
|
|
352
396
|
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
397
|
+
ensure
|
|
398
|
+
FileUtils.rm(fn) rescue nil
|
|
353
399
|
end
|
|
354
400
|
|
|
355
401
|
def test_fiber_scheduler_mutex
|
|
@@ -359,24 +405,24 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
359
405
|
Fiber.schedule do
|
|
360
406
|
buf << 11
|
|
361
407
|
mutex.synchronize {
|
|
362
|
-
buf << [12, machine.
|
|
408
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
363
409
|
sleep 0.01
|
|
364
|
-
buf << [13, machine.
|
|
410
|
+
buf << [13, machine.metrics[:total_ops]]
|
|
365
411
|
}
|
|
366
412
|
buf << 14
|
|
367
413
|
end
|
|
368
|
-
assert_equal 1, machine.
|
|
414
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
369
415
|
|
|
370
416
|
Fiber.schedule do
|
|
371
417
|
buf << 21
|
|
372
418
|
mutex.synchronize {
|
|
373
|
-
buf << [22, machine.
|
|
419
|
+
buf << [22, machine.metrics[:total_ops]]
|
|
374
420
|
sleep 0.01
|
|
375
|
-
buf << [23, machine.
|
|
421
|
+
buf << [23, machine.metrics[:total_ops]]
|
|
376
422
|
}
|
|
377
423
|
buf << 24
|
|
378
424
|
end
|
|
379
|
-
assert_equal 1, machine.
|
|
425
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
380
426
|
|
|
381
427
|
@scheduler.join
|
|
382
428
|
assert_equal [11, [12, 0], 21, [13, 2], 14, [22, 2], [23, 4], 24], buf
|
|
@@ -394,16 +440,16 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
394
440
|
|
|
395
441
|
buf = []
|
|
396
442
|
Fiber.schedule do
|
|
397
|
-
buf << [11, machine.
|
|
443
|
+
buf << [11, machine.metrics[:total_ops]]
|
|
398
444
|
buf << queue.shift
|
|
399
|
-
buf << [12, machine.
|
|
445
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
400
446
|
end
|
|
401
447
|
Fiber.schedule do
|
|
402
|
-
buf << [21, machine.
|
|
448
|
+
buf << [21, machine.metrics[:total_ops]]
|
|
403
449
|
queue << :foo
|
|
404
|
-
buf << [22, machine.
|
|
450
|
+
buf << [22, machine.metrics[:total_ops]]
|
|
405
451
|
end
|
|
406
|
-
assert_equal 0, machine.
|
|
452
|
+
assert_equal 0, machine.metrics[:total_ops]
|
|
407
453
|
@scheduler.join
|
|
408
454
|
|
|
409
455
|
assert_equal [[11, 0], [21, 0], [22, 0], :foo, [12, 1]], buf
|
|
@@ -420,14 +466,14 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
420
466
|
|
|
421
467
|
buf = []
|
|
422
468
|
Fiber.schedule do
|
|
423
|
-
buf << [11, machine.
|
|
469
|
+
buf << [11, machine.metrics[:total_ops]]
|
|
424
470
|
buf << queue.shift(timeout: 0.01)
|
|
425
|
-
buf << [12, machine.
|
|
471
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
426
472
|
end
|
|
427
473
|
Fiber.schedule do
|
|
428
|
-
buf << [21, machine.
|
|
474
|
+
buf << [21, machine.metrics[:total_ops]]
|
|
429
475
|
end
|
|
430
|
-
assert_equal 1, machine.
|
|
476
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
431
477
|
@scheduler.join
|
|
432
478
|
|
|
433
479
|
assert_equal [[11, 0], [21, 1], nil, [12, 2]], buf
|
|
@@ -447,10 +493,10 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
447
493
|
end
|
|
448
494
|
|
|
449
495
|
# No ops are issued, except for a NOP SQE used to wakeup the waiting thread.
|
|
450
|
-
assert_equal 0, machine.
|
|
496
|
+
assert_equal 0, machine.metrics[:total_ops]
|
|
451
497
|
|
|
452
498
|
@scheduler.join
|
|
453
|
-
assert_equal 1, machine.
|
|
499
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
454
500
|
assert_equal({
|
|
455
501
|
fiber: 1,
|
|
456
502
|
block: 1,
|
|
@@ -460,7 +506,8 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
460
506
|
end
|
|
461
507
|
|
|
462
508
|
def test_fiber_scheduler_system
|
|
463
|
-
skip
|
|
509
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
510
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
464
511
|
|
|
465
512
|
buf = []
|
|
466
513
|
Fiber.schedule do
|
|
@@ -478,13 +525,14 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
478
525
|
end
|
|
479
526
|
|
|
480
527
|
def test_fiber_scheduler_cmd
|
|
481
|
-
skip
|
|
528
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
529
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
482
530
|
|
|
483
531
|
buf = []
|
|
484
532
|
Fiber.schedule do
|
|
485
533
|
buf << `echo 'foo'`
|
|
486
534
|
end
|
|
487
|
-
assert_equal 1, machine.
|
|
535
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
488
536
|
@scheduler.join
|
|
489
537
|
assert_equal ["foo\n"], buf
|
|
490
538
|
assert_equal({
|
|
@@ -498,19 +546,20 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
498
546
|
end
|
|
499
547
|
|
|
500
548
|
def test_fiber_scheduler_popen
|
|
501
|
-
skip
|
|
549
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
550
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
502
551
|
|
|
503
552
|
buf = []
|
|
504
553
|
Fiber.schedule do
|
|
505
554
|
IO.popen('ruby', 'r+') do |pipe|
|
|
506
|
-
buf << [11, machine.
|
|
555
|
+
buf << [11, machine.metrics[:total_ops]]
|
|
507
556
|
pipe.puts 'puts "bar"'
|
|
508
|
-
buf << [12, machine.
|
|
557
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
509
558
|
pipe.close_write
|
|
510
|
-
buf << [13, pipe.gets.chomp, machine.
|
|
559
|
+
buf << [13, pipe.gets.chomp, machine.metrics[:total_ops]]
|
|
511
560
|
end
|
|
512
561
|
end
|
|
513
|
-
assert_equal 1, machine.
|
|
562
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
514
563
|
@scheduler.join
|
|
515
564
|
assert_equal [[11, 0], [12, 3], [13, "bar", 5]], buf
|
|
516
565
|
assert_equal({
|
|
@@ -535,7 +584,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
535
584
|
rescue Exception => e
|
|
536
585
|
exception = e
|
|
537
586
|
end
|
|
538
|
-
assert_equal 1, machine.
|
|
587
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
539
588
|
machine.snooze
|
|
540
589
|
Thread.new {
|
|
541
590
|
r.close
|
|
@@ -558,7 +607,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
558
607
|
Fiber.schedule do
|
|
559
608
|
addrs = Addrinfo.getaddrinfo("localhost", 80, Socket::AF_INET, :STREAM)
|
|
560
609
|
end
|
|
561
|
-
assert_equal 1, machine.
|
|
610
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
562
611
|
@scheduler.join
|
|
563
612
|
assert_kind_of Array, addrs
|
|
564
613
|
addr = addrs.first
|
|
@@ -567,6 +616,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
567
616
|
assert_equal({
|
|
568
617
|
fiber: 1,
|
|
569
618
|
io_read: 2,
|
|
619
|
+
io_close: 1,
|
|
570
620
|
blocking_operation_wait: 1,
|
|
571
621
|
address_resolve: 1,
|
|
572
622
|
join: 1
|
|
@@ -584,7 +634,7 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
584
634
|
res = e
|
|
585
635
|
end
|
|
586
636
|
@scheduler.join
|
|
587
|
-
assert_equal 3, machine.
|
|
637
|
+
assert_equal 3, machine.metrics[:total_ops]
|
|
588
638
|
assert_kind_of Timeout::Error, res
|
|
589
639
|
assert_equal({
|
|
590
640
|
fiber: 1,
|
|
@@ -638,3 +688,856 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
638
688
|
assert_equal (1..20).map { it * 10 }, buf.sort
|
|
639
689
|
end
|
|
640
690
|
end
|
|
691
|
+
|
|
692
|
+
class FiberSchedulerIOClassMethodsTest < UMBaseTest
|
|
693
|
+
def setup
|
|
694
|
+
super
|
|
695
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
696
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
697
|
+
Fiber.set_scheduler(@scheduler)
|
|
698
|
+
@fn = "/tmp/um_#{SecureRandom.hex}"
|
|
699
|
+
IO.write(@fn, '===')
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def teardown
|
|
703
|
+
FileUtils.rm(@fn) rescue nil
|
|
704
|
+
Fiber.set_scheduler(nil)
|
|
705
|
+
super
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
def test_IO_s_binread
|
|
709
|
+
ret = nil
|
|
710
|
+
Fiber.schedule do
|
|
711
|
+
ret = IO.binread(@fn)
|
|
712
|
+
end
|
|
713
|
+
@scheduler.join
|
|
714
|
+
assert_equal '===', ret
|
|
715
|
+
assert_equal({
|
|
716
|
+
fiber: 1,
|
|
717
|
+
io_read: 2,
|
|
718
|
+
blocking_operation_wait: 1,
|
|
719
|
+
io_close: 1,
|
|
720
|
+
join: 1
|
|
721
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
def test_IO_s_binwrite
|
|
725
|
+
ret = nil
|
|
726
|
+
Fiber.schedule do
|
|
727
|
+
ret = IO.binwrite(@fn, '***', 2)
|
|
728
|
+
end
|
|
729
|
+
@scheduler.join
|
|
730
|
+
assert_equal 3, ret
|
|
731
|
+
assert_equal '==***', IO.read(@fn)
|
|
732
|
+
assert_equal({
|
|
733
|
+
fiber: 1,
|
|
734
|
+
blocking_operation_wait: 1,
|
|
735
|
+
io_close: 1,
|
|
736
|
+
join: 1
|
|
737
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
def test_IO_s_copy_stream
|
|
741
|
+
fn2 = "/tmp/um_#{SecureRandom.hex}"
|
|
742
|
+
ret = nil
|
|
743
|
+
Fiber.schedule do
|
|
744
|
+
ret = IO.copy_stream(@fn, fn2)
|
|
745
|
+
end
|
|
746
|
+
@scheduler.join
|
|
747
|
+
assert_equal 3, ret
|
|
748
|
+
assert_equal '===', IO.read(fn2)
|
|
749
|
+
assert_equal({
|
|
750
|
+
fiber: 1,
|
|
751
|
+
blocking_operation_wait: 3,
|
|
752
|
+
io_close: 2,
|
|
753
|
+
join: 1
|
|
754
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
755
|
+
ensure
|
|
756
|
+
FileUtils.rm(fn2) rescue nil
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
def test_IO_s_foreach
|
|
760
|
+
buf = []
|
|
761
|
+
Fiber.schedule do
|
|
762
|
+
IO.foreach(@fn) { buf << it }
|
|
763
|
+
end
|
|
764
|
+
@scheduler.join
|
|
765
|
+
assert_equal ['==='], buf
|
|
766
|
+
assert_equal({
|
|
767
|
+
fiber: 1,
|
|
768
|
+
io_read: 3,
|
|
769
|
+
blocking_operation_wait: 1,
|
|
770
|
+
io_close: 1,
|
|
771
|
+
join: 1
|
|
772
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
def test_IO_s_foreach
|
|
776
|
+
buf = []
|
|
777
|
+
Fiber.schedule do
|
|
778
|
+
IO.foreach(@fn) { buf << it }
|
|
779
|
+
end
|
|
780
|
+
@scheduler.join
|
|
781
|
+
assert_equal ['==='], buf
|
|
782
|
+
assert_equal({
|
|
783
|
+
fiber: 1,
|
|
784
|
+
io_read: 3,
|
|
785
|
+
blocking_operation_wait: 1,
|
|
786
|
+
io_close: 1,
|
|
787
|
+
join: 1
|
|
788
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def test_IO_s_popen
|
|
792
|
+
ret = nil
|
|
793
|
+
Fiber.schedule do
|
|
794
|
+
IO.popen("cat #{@fn}") { ret = it.read }
|
|
795
|
+
end
|
|
796
|
+
@scheduler.join
|
|
797
|
+
assert_equal '===', ret
|
|
798
|
+
assert_equal({
|
|
799
|
+
fiber: 1,
|
|
800
|
+
io_read: 2,
|
|
801
|
+
io_close: 1,
|
|
802
|
+
join: 1
|
|
803
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
def test_IO_s_read
|
|
807
|
+
ret = nil
|
|
808
|
+
Fiber.schedule do
|
|
809
|
+
ret = IO.read(@fn)
|
|
810
|
+
end
|
|
811
|
+
@scheduler.join
|
|
812
|
+
assert_equal '===', ret
|
|
813
|
+
assert_equal({
|
|
814
|
+
fiber: 1,
|
|
815
|
+
io_read: 2,
|
|
816
|
+
blocking_operation_wait: 1,
|
|
817
|
+
io_close: 1,
|
|
818
|
+
join: 1
|
|
819
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def test_IO_s_readlines
|
|
823
|
+
ret = nil
|
|
824
|
+
Fiber.schedule do
|
|
825
|
+
ret = IO.readlines(@fn)
|
|
826
|
+
end
|
|
827
|
+
@scheduler.join
|
|
828
|
+
assert_equal ['==='], ret
|
|
829
|
+
assert_equal({
|
|
830
|
+
fiber: 1,
|
|
831
|
+
io_read: 3,
|
|
832
|
+
blocking_operation_wait: 1,
|
|
833
|
+
io_close: 1,
|
|
834
|
+
join: 1
|
|
835
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
def test_IO_s_select
|
|
839
|
+
ret = nil
|
|
840
|
+
io = File.open(@fn, 'r+')
|
|
841
|
+
Fiber.schedule do
|
|
842
|
+
ret = IO.select([io], [io], [])
|
|
843
|
+
end
|
|
844
|
+
@scheduler.join
|
|
845
|
+
assert_equal [[io], [io], []], ret
|
|
846
|
+
assert_equal({
|
|
847
|
+
fiber: 1,
|
|
848
|
+
io_select: 1,
|
|
849
|
+
join: 1
|
|
850
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
851
|
+
ensure
|
|
852
|
+
io.close rescue nil
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
def test_IO_s_write
|
|
856
|
+
ret = nil
|
|
857
|
+
Fiber.schedule do
|
|
858
|
+
ret = IO.write(@fn, '***', 2)
|
|
859
|
+
end
|
|
860
|
+
@scheduler.join
|
|
861
|
+
assert_equal 3, ret
|
|
862
|
+
assert_equal '==***', IO.read(@fn)
|
|
863
|
+
assert_equal({
|
|
864
|
+
fiber: 1,
|
|
865
|
+
blocking_operation_wait: 1,
|
|
866
|
+
io_close: 1,
|
|
867
|
+
join: 1
|
|
868
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
class FiberSchedulerIOInstanceMethodsTest < UMBaseTest
|
|
873
|
+
def setup
|
|
874
|
+
super
|
|
875
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
876
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
877
|
+
Fiber.set_scheduler(@scheduler)
|
|
878
|
+
@fn = "/tmp/um_#{SecureRandom.hex}"
|
|
879
|
+
IO.write(@fn, '===')
|
|
880
|
+
@io = File.open(@fn, 'r+')
|
|
881
|
+
@io.sync = true
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
def teardown
|
|
885
|
+
@io.close rescue nil
|
|
886
|
+
FileUtils.rm(@fn) rescue nil
|
|
887
|
+
Fiber.set_scheduler(nil)
|
|
888
|
+
super
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
def test_IO_i_double_left_chevron
|
|
892
|
+
Fiber.schedule do
|
|
893
|
+
@io.seek(2)
|
|
894
|
+
@io << '***'
|
|
895
|
+
end
|
|
896
|
+
@scheduler.join
|
|
897
|
+
assert_equal '==***', IO.read(@fn)
|
|
898
|
+
assert_equal({
|
|
899
|
+
fiber: 1,
|
|
900
|
+
io_write: 1,
|
|
901
|
+
join: 1
|
|
902
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
def test_IO_i_close
|
|
906
|
+
ret = nil
|
|
907
|
+
Fiber.schedule do
|
|
908
|
+
ret = @io.close
|
|
909
|
+
end
|
|
910
|
+
@scheduler.join
|
|
911
|
+
assert_nil ret
|
|
912
|
+
assert_equal({
|
|
913
|
+
fiber: 1,
|
|
914
|
+
io_close: 1,
|
|
915
|
+
join: 1
|
|
916
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
def test_IO_i_close_read
|
|
920
|
+
ret = nil
|
|
921
|
+
Fiber.schedule do
|
|
922
|
+
ret = @io.close_read
|
|
923
|
+
end
|
|
924
|
+
@scheduler.join
|
|
925
|
+
assert_nil ret
|
|
926
|
+
assert_equal({
|
|
927
|
+
fiber: 1,
|
|
928
|
+
join: 1
|
|
929
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
def test_IO_i_close_write
|
|
933
|
+
ret = nil
|
|
934
|
+
Fiber.schedule do
|
|
935
|
+
ret = @io.close_write
|
|
936
|
+
end
|
|
937
|
+
@scheduler.join
|
|
938
|
+
assert_nil ret
|
|
939
|
+
assert_equal({
|
|
940
|
+
fiber: 1,
|
|
941
|
+
join: 1
|
|
942
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
def test_IO_i_each_byte
|
|
946
|
+
buf = []
|
|
947
|
+
Fiber.schedule do
|
|
948
|
+
@io.each_byte { buf << it }
|
|
949
|
+
end
|
|
950
|
+
@scheduler.join
|
|
951
|
+
assert_equal [61, 61, 61], buf
|
|
952
|
+
assert_equal({
|
|
953
|
+
fiber: 1,
|
|
954
|
+
io_read: 2,
|
|
955
|
+
join: 1
|
|
956
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
def test_IO_i_each_char
|
|
960
|
+
buf = []
|
|
961
|
+
Fiber.schedule do
|
|
962
|
+
@io.each_char { buf << it }
|
|
963
|
+
end
|
|
964
|
+
@scheduler.join
|
|
965
|
+
assert_equal ['=', '=', '='], buf
|
|
966
|
+
assert_equal({
|
|
967
|
+
fiber: 1,
|
|
968
|
+
io_read: 2,
|
|
969
|
+
join: 1
|
|
970
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
def test_IO_i_each_line
|
|
974
|
+
buf = []
|
|
975
|
+
Fiber.schedule do
|
|
976
|
+
@io.each_line { buf << it }
|
|
977
|
+
end
|
|
978
|
+
@scheduler.join
|
|
979
|
+
assert_equal ['==='], buf
|
|
980
|
+
assert_equal({
|
|
981
|
+
fiber: 1,
|
|
982
|
+
io_read: 3,
|
|
983
|
+
join: 1
|
|
984
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
def test_IO_i_getbyte
|
|
988
|
+
ret = nil
|
|
989
|
+
Fiber.schedule do
|
|
990
|
+
ret = @io.getbyte
|
|
991
|
+
end
|
|
992
|
+
@scheduler.join
|
|
993
|
+
assert_equal 61, ret
|
|
994
|
+
assert_equal({
|
|
995
|
+
fiber: 1,
|
|
996
|
+
io_read: 1,
|
|
997
|
+
join: 1
|
|
998
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
def test_IO_i_getc
|
|
1002
|
+
ret = nil
|
|
1003
|
+
Fiber.schedule do
|
|
1004
|
+
ret = @io.getc
|
|
1005
|
+
end
|
|
1006
|
+
@scheduler.join
|
|
1007
|
+
assert_equal '=', ret
|
|
1008
|
+
assert_equal({
|
|
1009
|
+
fiber: 1,
|
|
1010
|
+
io_read: 1,
|
|
1011
|
+
join: 1
|
|
1012
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
def test_IO_i_gets
|
|
1016
|
+
ret = nil
|
|
1017
|
+
Fiber.schedule do
|
|
1018
|
+
ret = @io.gets
|
|
1019
|
+
end
|
|
1020
|
+
@scheduler.join
|
|
1021
|
+
assert_equal '===', ret
|
|
1022
|
+
assert_equal({
|
|
1023
|
+
fiber: 1,
|
|
1024
|
+
io_read: 2,
|
|
1025
|
+
join: 1
|
|
1026
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
def test_IO_i_pread
|
|
1030
|
+
ret = nil
|
|
1031
|
+
Fiber.schedule do
|
|
1032
|
+
ret = @io.pread(5, 2)
|
|
1033
|
+
end
|
|
1034
|
+
@scheduler.join
|
|
1035
|
+
assert_equal '=', ret
|
|
1036
|
+
assert_equal({
|
|
1037
|
+
fiber: 1,
|
|
1038
|
+
io_pread: 1,
|
|
1039
|
+
join: 1
|
|
1040
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
def test_IO_i_print
|
|
1044
|
+
ret = nil
|
|
1045
|
+
Fiber.schedule do
|
|
1046
|
+
ret = @io.print(1, 2.0, '3')
|
|
1047
|
+
end
|
|
1048
|
+
@scheduler.join
|
|
1049
|
+
assert_nil ret
|
|
1050
|
+
assert_equal '12.03', IO.read(@fn)
|
|
1051
|
+
assert_equal({
|
|
1052
|
+
fiber: 1,
|
|
1053
|
+
io_write: 3,
|
|
1054
|
+
join: 1
|
|
1055
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
def test_IO_i_printf
|
|
1059
|
+
ret = nil
|
|
1060
|
+
Fiber.schedule do
|
|
1061
|
+
ret = @io.printf('%08x', 4321)
|
|
1062
|
+
end
|
|
1063
|
+
@scheduler.join
|
|
1064
|
+
assert_nil ret
|
|
1065
|
+
assert_equal '000010e1', IO.read(@fn)
|
|
1066
|
+
assert_equal({
|
|
1067
|
+
fiber: 1,
|
|
1068
|
+
io_write: 1,
|
|
1069
|
+
join: 1
|
|
1070
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
def test_IO_i_putc
|
|
1074
|
+
ret = nil
|
|
1075
|
+
Fiber.schedule do
|
|
1076
|
+
ret = @io.putc('B')
|
|
1077
|
+
end
|
|
1078
|
+
@scheduler.join
|
|
1079
|
+
assert_equal 'B', ret
|
|
1080
|
+
assert_equal 'B==', IO.read(@fn)
|
|
1081
|
+
assert_equal({
|
|
1082
|
+
fiber: 1,
|
|
1083
|
+
io_write: 1,
|
|
1084
|
+
join: 1
|
|
1085
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
def test_IO_i_puts
|
|
1089
|
+
ret = nil
|
|
1090
|
+
Fiber.schedule do
|
|
1091
|
+
ret = @io.puts('abc')
|
|
1092
|
+
end
|
|
1093
|
+
@scheduler.join
|
|
1094
|
+
assert_nil ret
|
|
1095
|
+
assert_equal "abc\n", IO.read(@fn)
|
|
1096
|
+
assert_equal({
|
|
1097
|
+
fiber: 1,
|
|
1098
|
+
io_write: 2,
|
|
1099
|
+
join: 1
|
|
1100
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
def test_IO_i_pwrite
|
|
1104
|
+
ret = nil
|
|
1105
|
+
Fiber.schedule do
|
|
1106
|
+
ret = @io.pwrite('abc', 2)
|
|
1107
|
+
end
|
|
1108
|
+
@scheduler.join
|
|
1109
|
+
assert_equal 3, ret
|
|
1110
|
+
assert_equal "==abc", IO.read(@fn)
|
|
1111
|
+
assert_equal({
|
|
1112
|
+
fiber: 1,
|
|
1113
|
+
io_pwrite: 1,
|
|
1114
|
+
join: 1
|
|
1115
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
def test_IO_i_read
|
|
1119
|
+
ret = nil
|
|
1120
|
+
Fiber.schedule do
|
|
1121
|
+
ret = @io.read
|
|
1122
|
+
end
|
|
1123
|
+
@scheduler.join
|
|
1124
|
+
assert_equal "===", ret
|
|
1125
|
+
assert_equal({
|
|
1126
|
+
fiber: 1,
|
|
1127
|
+
io_read: 2,
|
|
1128
|
+
join: 1
|
|
1129
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
def test_IO_i_readbyte
|
|
1133
|
+
ret = nil
|
|
1134
|
+
Fiber.schedule do
|
|
1135
|
+
ret = @io.readbyte
|
|
1136
|
+
end
|
|
1137
|
+
@scheduler.join
|
|
1138
|
+
assert_equal 61, ret
|
|
1139
|
+
assert_equal({
|
|
1140
|
+
fiber: 1,
|
|
1141
|
+
io_read: 1,
|
|
1142
|
+
join: 1
|
|
1143
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
def test_IO_i_readchar
|
|
1147
|
+
ret = nil
|
|
1148
|
+
Fiber.schedule do
|
|
1149
|
+
ret = @io.readchar
|
|
1150
|
+
end
|
|
1151
|
+
@scheduler.join
|
|
1152
|
+
assert_equal '=', ret
|
|
1153
|
+
assert_equal({
|
|
1154
|
+
fiber: 1,
|
|
1155
|
+
io_read: 1,
|
|
1156
|
+
join: 1
|
|
1157
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
def test_IO_i_readline
|
|
1161
|
+
ret = nil
|
|
1162
|
+
Fiber.schedule do
|
|
1163
|
+
ret = @io.readline
|
|
1164
|
+
end
|
|
1165
|
+
@scheduler.join
|
|
1166
|
+
assert_equal '===', ret
|
|
1167
|
+
assert_equal({
|
|
1168
|
+
fiber: 1,
|
|
1169
|
+
io_read: 2,
|
|
1170
|
+
join: 1
|
|
1171
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
def test_IO_i_readlines
|
|
1175
|
+
ret = nil
|
|
1176
|
+
Fiber.schedule do
|
|
1177
|
+
ret = @io.readlines
|
|
1178
|
+
end
|
|
1179
|
+
@scheduler.join
|
|
1180
|
+
assert_equal ["==="], ret
|
|
1181
|
+
assert_equal({
|
|
1182
|
+
fiber: 1,
|
|
1183
|
+
io_read: 3,
|
|
1184
|
+
join: 1
|
|
1185
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
def test_IO_i_readpartial
|
|
1189
|
+
ret = nil
|
|
1190
|
+
Fiber.schedule do
|
|
1191
|
+
ret = @io.readpartial(7)
|
|
1192
|
+
end
|
|
1193
|
+
@scheduler.join
|
|
1194
|
+
assert_equal "===", ret
|
|
1195
|
+
assert_equal({
|
|
1196
|
+
fiber: 1,
|
|
1197
|
+
io_read: 1,
|
|
1198
|
+
join: 1
|
|
1199
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1200
|
+
end
|
|
1201
|
+
|
|
1202
|
+
def test_IO_i_sysread
|
|
1203
|
+
ret = nil
|
|
1204
|
+
Fiber.schedule do
|
|
1205
|
+
ret = @io.sysread(7)
|
|
1206
|
+
end
|
|
1207
|
+
@scheduler.join
|
|
1208
|
+
assert_equal "===", ret
|
|
1209
|
+
assert_equal({
|
|
1210
|
+
fiber: 1,
|
|
1211
|
+
io_read: 1,
|
|
1212
|
+
join: 1
|
|
1213
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1214
|
+
end
|
|
1215
|
+
|
|
1216
|
+
def test_IO_i_syswrite
|
|
1217
|
+
ret = nil
|
|
1218
|
+
Fiber.schedule do
|
|
1219
|
+
ret = @io.syswrite('2')
|
|
1220
|
+
end
|
|
1221
|
+
@scheduler.join
|
|
1222
|
+
assert_equal 1, ret
|
|
1223
|
+
assert_equal '2==', IO.read(@fn)
|
|
1224
|
+
assert_equal({
|
|
1225
|
+
fiber: 1,
|
|
1226
|
+
io_write: 1,
|
|
1227
|
+
join: 1
|
|
1228
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1229
|
+
end
|
|
1230
|
+
|
|
1231
|
+
def test_IO_i_wait
|
|
1232
|
+
ret = nil
|
|
1233
|
+
Fiber.schedule do
|
|
1234
|
+
ret = @io.wait(IO::READABLE | IO::WRITABLE)
|
|
1235
|
+
end
|
|
1236
|
+
@scheduler.join
|
|
1237
|
+
assert_equal @io, ret
|
|
1238
|
+
assert_equal({
|
|
1239
|
+
fiber: 1,
|
|
1240
|
+
io_wait: 1,
|
|
1241
|
+
join: 1
|
|
1242
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
def test_IO_i_wait_pipe_read_timeout
|
|
1246
|
+
r, w = IO.pipe
|
|
1247
|
+
ret = nil
|
|
1248
|
+
Fiber.schedule do
|
|
1249
|
+
ret = r.wait(IO::READABLE, 0.05)
|
|
1250
|
+
end
|
|
1251
|
+
@scheduler.join
|
|
1252
|
+
assert_nil ret
|
|
1253
|
+
assert_equal({
|
|
1254
|
+
fiber: 1,
|
|
1255
|
+
io_wait: 1,
|
|
1256
|
+
join: 1
|
|
1257
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1258
|
+
ensure
|
|
1259
|
+
r.close rescue nil
|
|
1260
|
+
w.close rescue nil
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
def test_IO_i_wait_readable
|
|
1264
|
+
ret = nil
|
|
1265
|
+
Fiber.schedule do
|
|
1266
|
+
ret = @io.wait_readable
|
|
1267
|
+
end
|
|
1268
|
+
@scheduler.join
|
|
1269
|
+
assert_equal @io, ret
|
|
1270
|
+
assert_equal({
|
|
1271
|
+
fiber: 1,
|
|
1272
|
+
io_wait: 1,
|
|
1273
|
+
join: 1
|
|
1274
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
def test_IO_i_wait_writable
|
|
1278
|
+
ret = nil
|
|
1279
|
+
Fiber.schedule do
|
|
1280
|
+
ret = @io.wait_writable
|
|
1281
|
+
end
|
|
1282
|
+
@scheduler.join
|
|
1283
|
+
assert_equal @io, ret
|
|
1284
|
+
assert_equal({
|
|
1285
|
+
fiber: 1,
|
|
1286
|
+
io_wait: 1,
|
|
1287
|
+
join: 1
|
|
1288
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1291
|
+
def test_IO_i_write
|
|
1292
|
+
ret = nil
|
|
1293
|
+
Fiber.schedule do
|
|
1294
|
+
ret = @io.write('abcde')
|
|
1295
|
+
end
|
|
1296
|
+
@scheduler.join
|
|
1297
|
+
assert_equal 5, ret
|
|
1298
|
+
assert_equal 'abcde', IO.read(@fn)
|
|
1299
|
+
assert_equal({
|
|
1300
|
+
fiber: 1,
|
|
1301
|
+
io_write: 1,
|
|
1302
|
+
join: 1
|
|
1303
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1304
|
+
end
|
|
1305
|
+
end
|
|
1306
|
+
|
|
1307
|
+
class FiberSchedulerQueueTest < UMBaseTest
|
|
1308
|
+
def setup
|
|
1309
|
+
super
|
|
1310
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
1311
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
1312
|
+
Fiber.set_scheduler(@scheduler)
|
|
1313
|
+
@queue = Queue.new
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
def teardown
|
|
1317
|
+
Fiber.set_scheduler(nil)
|
|
1318
|
+
super
|
|
1319
|
+
end
|
|
1320
|
+
|
|
1321
|
+
I = 5
|
|
1322
|
+
C = 5
|
|
1323
|
+
|
|
1324
|
+
def test_fiber_scheduler_queue_multi_fiber
|
|
1325
|
+
C.times {
|
|
1326
|
+
Fiber.schedule do
|
|
1327
|
+
I.times { sleep 0.0001; @queue << rand(1000) }
|
|
1328
|
+
end
|
|
1329
|
+
}
|
|
1330
|
+
C.times {
|
|
1331
|
+
Fiber.schedule do
|
|
1332
|
+
I.times { @queue.shift; @scheduler.yield; }
|
|
1333
|
+
end
|
|
1334
|
+
}
|
|
1335
|
+
@scheduler.join
|
|
1336
|
+
assert_equal({
|
|
1337
|
+
fiber: 10,
|
|
1338
|
+
kernel_sleep: 25,
|
|
1339
|
+
yield: 25,
|
|
1340
|
+
block: 25,
|
|
1341
|
+
unblock: 25,
|
|
1342
|
+
join: 1
|
|
1343
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1344
|
+
end
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
class FiberSchedulerNetHTTPTest < UMBaseTest
|
|
1348
|
+
def setup
|
|
1349
|
+
super
|
|
1350
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
1351
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
1352
|
+
Fiber.set_scheduler(@scheduler)
|
|
1353
|
+
@uri = URI('https://ipinfo.io/')
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1356
|
+
def teardown
|
|
1357
|
+
Fiber.set_scheduler(nil)
|
|
1358
|
+
super
|
|
1359
|
+
end
|
|
1360
|
+
|
|
1361
|
+
C = 10
|
|
1362
|
+
|
|
1363
|
+
def test_fiber_scheduler_net_http
|
|
1364
|
+
buf = []
|
|
1365
|
+
C.times {
|
|
1366
|
+
Fiber.schedule do
|
|
1367
|
+
buf << JSON.parse(Net::HTTP.get(@uri))
|
|
1368
|
+
end
|
|
1369
|
+
}
|
|
1370
|
+
@scheduler.join
|
|
1371
|
+
assert_equal C, buf.size
|
|
1372
|
+
assert_equal 1, buf.map { it['ip'] }.uniq.compact.size
|
|
1373
|
+
calls = @scheduler.calls.map { it[:sym] }.tally
|
|
1374
|
+
assert_equal C, calls[:fiber]
|
|
1375
|
+
assert_equal C, calls[:io_close]
|
|
1376
|
+
assert_in_range (C * 2)..(C * 4), calls[:io_wait]
|
|
1377
|
+
assert_in_range (C * 7)..(C * 17), calls[:blocking_operation_wait]
|
|
1378
|
+
end
|
|
1379
|
+
end
|
|
1380
|
+
|
|
1381
|
+
class FiberSchedulerMultiPipeTest < UMBaseTest
|
|
1382
|
+
def setup
|
|
1383
|
+
super
|
|
1384
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
1385
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
1386
|
+
Fiber.set_scheduler(@scheduler)
|
|
1387
|
+
@uri = URI('https://ipinfo.io/')
|
|
1388
|
+
end
|
|
1389
|
+
|
|
1390
|
+
def teardown
|
|
1391
|
+
Fiber.set_scheduler(nil)
|
|
1392
|
+
super
|
|
1393
|
+
end
|
|
1394
|
+
|
|
1395
|
+
C = 10
|
|
1396
|
+
I = 100
|
|
1397
|
+
SIZE = 1024
|
|
1398
|
+
DATA = '*' * SIZE
|
|
1399
|
+
|
|
1400
|
+
def test_fiber_scheduler_multi_pipe
|
|
1401
|
+
count = 0
|
|
1402
|
+
ios = []
|
|
1403
|
+
C.times do
|
|
1404
|
+
r, w = IO.pipe
|
|
1405
|
+
r.sync = true
|
|
1406
|
+
w.sync = true
|
|
1407
|
+
ios << r << w
|
|
1408
|
+
Fiber.schedule do
|
|
1409
|
+
I.times { w.write(DATA); count += 1 }
|
|
1410
|
+
w.close
|
|
1411
|
+
end
|
|
1412
|
+
Fiber.schedule do
|
|
1413
|
+
I.times { r.read(SIZE); count += 1 }
|
|
1414
|
+
end
|
|
1415
|
+
end
|
|
1416
|
+
@scheduler.join
|
|
1417
|
+
assert_equal 2000, count
|
|
1418
|
+
assert_equal({
|
|
1419
|
+
fiber: 20,
|
|
1420
|
+
io_read: 1000,
|
|
1421
|
+
io_write: 1000,
|
|
1422
|
+
io_close: 10,
|
|
1423
|
+
join: 1
|
|
1424
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
1425
|
+
ensure
|
|
1426
|
+
ios.each { it.close rescue nil }
|
|
1427
|
+
end
|
|
1428
|
+
end
|
|
1429
|
+
|
|
1430
|
+
class FiberSchedulerMultiTCPTest < UMBaseTest
|
|
1431
|
+
C = 10
|
|
1432
|
+
W = 10
|
|
1433
|
+
I = 10
|
|
1434
|
+
SIZE = 1024
|
|
1435
|
+
DATA = '*' * SIZE
|
|
1436
|
+
|
|
1437
|
+
def p(o)
|
|
1438
|
+
# UM.debug(o.inspect)
|
|
1439
|
+
end
|
|
1440
|
+
|
|
1441
|
+
def run_server(port)
|
|
1442
|
+
server = TCPServer.new('127.0.0.1', port)
|
|
1443
|
+
p(server:)
|
|
1444
|
+
|
|
1445
|
+
server_fiber = Fiber.schedule { accept_loop(server) }
|
|
1446
|
+
|
|
1447
|
+
fibers = []
|
|
1448
|
+
W.times {
|
|
1449
|
+
fibers << Fiber.schedule { run_client(port) }
|
|
1450
|
+
}
|
|
1451
|
+
@machine.await_fibers(fibers)
|
|
1452
|
+
server.close
|
|
1453
|
+
@machine.await_fibers(server_fiber)
|
|
1454
|
+
rescue Exception => e
|
|
1455
|
+
p test_run_server: e
|
|
1456
|
+
p e.backtrace
|
|
1457
|
+
exit!
|
|
1458
|
+
end
|
|
1459
|
+
|
|
1460
|
+
def accept_loop(server)
|
|
1461
|
+
loop do
|
|
1462
|
+
conn = server.accept
|
|
1463
|
+
p(accept: conn)
|
|
1464
|
+
Fiber.schedule { conn_loop(conn) }
|
|
1465
|
+
end
|
|
1466
|
+
rescue IOError
|
|
1467
|
+
# socket closed
|
|
1468
|
+
rescue Exception => e
|
|
1469
|
+
p accept_loop: e
|
|
1470
|
+
p e.backtrace
|
|
1471
|
+
exit!
|
|
1472
|
+
end
|
|
1473
|
+
|
|
1474
|
+
def conn_loop(conn)
|
|
1475
|
+
loop do
|
|
1476
|
+
msg = conn.recv(SIZE * 2)
|
|
1477
|
+
break if !msg
|
|
1478
|
+
|
|
1479
|
+
conn.send(msg, 0)
|
|
1480
|
+
end
|
|
1481
|
+
rescue Exception => e
|
|
1482
|
+
p conn_loop: e
|
|
1483
|
+
p e.backtrace
|
|
1484
|
+
ensure
|
|
1485
|
+
conn.close rescue nil
|
|
1486
|
+
end
|
|
1487
|
+
|
|
1488
|
+
def run_client(port)
|
|
1489
|
+
client = TCPSocket.new('127.0.0.1', port)
|
|
1490
|
+
p(client:)
|
|
1491
|
+
I.times do
|
|
1492
|
+
client.send(DATA, 0)
|
|
1493
|
+
client.recv(SIZE * 2)
|
|
1494
|
+
@msg_count += 1
|
|
1495
|
+
end
|
|
1496
|
+
p(:client_done)
|
|
1497
|
+
rescue Exception => e
|
|
1498
|
+
p client_loop: e
|
|
1499
|
+
p e.backtrace
|
|
1500
|
+
ensure
|
|
1501
|
+
client.close
|
|
1502
|
+
end
|
|
1503
|
+
|
|
1504
|
+
def run_fiber_scheduler_multi_tcp
|
|
1505
|
+
@msg_count = 0
|
|
1506
|
+
STDOUT.sync = true
|
|
1507
|
+
ios = []
|
|
1508
|
+
fibers = []
|
|
1509
|
+
C.times do |i|
|
|
1510
|
+
port = @port_base + i
|
|
1511
|
+
fibers << Fiber.schedule do
|
|
1512
|
+
run_server(port)
|
|
1513
|
+
end
|
|
1514
|
+
end
|
|
1515
|
+
@machine.await_fibers(fibers)
|
|
1516
|
+
@calls = @scheduler.calls.map { it[:sym] }.tally
|
|
1517
|
+
ensure
|
|
1518
|
+
# p ensure: ios
|
|
1519
|
+
ios.each { it.close rescue nil }
|
|
1520
|
+
end
|
|
1521
|
+
|
|
1522
|
+
def test_fiber_scheduler_multi_tcp
|
|
1523
|
+
Thread.new do
|
|
1524
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
1525
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
1526
|
+
Fiber.set_scheduler(@scheduler)
|
|
1527
|
+
@port_base = assign_port
|
|
1528
|
+
run_fiber_scheduler_multi_tcp
|
|
1529
|
+
ensure
|
|
1530
|
+
Fiber.set_scheduler(nil)
|
|
1531
|
+
end.join
|
|
1532
|
+
|
|
1533
|
+
assert_equal 1000, @msg_count
|
|
1534
|
+
assert_equal({
|
|
1535
|
+
fiber: 220,
|
|
1536
|
+
io_wait: 2120,
|
|
1537
|
+
io_close: 210,
|
|
1538
|
+
fiber_interrupt: 10,
|
|
1539
|
+
unblock: 10,
|
|
1540
|
+
kernel_sleep: 10
|
|
1541
|
+
}, @calls)
|
|
1542
|
+
end
|
|
1543
|
+
end
|