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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +3 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +34 -0
- data/TODO.md +132 -26
- 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} +6 -3
- data/ext/um/extconf.rb +1 -1
- data/ext/um/um.c +404 -95
- data/ext/um/um.h +77 -24
- data/ext/um/um_async_op.c +2 -2
- data/ext/um/um_class.c +168 -18
- data/ext/um/um_op.c +43 -0
- data/ext/um/um_sync.c +10 -16
- data/ext/um/um_utils.c +16 -0
- data/grant-2025/journal.md +242 -1
- data/grant-2025/tasks.md +136 -41
- data/lib/uringmachine/actor.rb +8 -0
- data/lib/uringmachine/dns_resolver.rb +1 -2
- data/lib/uringmachine/fiber_scheduler.rb +283 -110
- 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 +1425 -20
- data/test/test_um.rb +565 -113
- data/uringmachine.gemspec +6 -5
- data/vendor/liburing/src/include/liburing/io_uring.h +1 -0
- data/vendor/liburing/src/include/liburing.h +13 -0
- data/vendor/liburing/src/liburing-ffi.map +1 -0
- data/vendor/liburing/test/bind-listen.c +175 -13
- data/vendor/liburing/test/read-write.c +4 -4
- data/vendor/liburing/test/ringbuf-read.c +4 -4
- data/vendor/liburing/test/send_recv.c +8 -7
- metadata +50 -28
- data/examples/bm_fileno.rb +0 -33
- data/examples/bm_queue.rb +0 -110
- 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
|
@@ -2,17 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'helper'
|
|
4
4
|
require 'uringmachine/fiber_scheduler'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
require 'socket'
|
|
7
|
+
require 'net/http'
|
|
8
|
+
require 'json'
|
|
9
|
+
|
|
10
|
+
class MethodCallAuditor
|
|
11
|
+
attr_reader :calls
|
|
12
|
+
|
|
13
|
+
def initialize(target)
|
|
14
|
+
@target = target
|
|
15
|
+
@calls = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def respond_to?(sym, include_all = false) = @target.respond_to?(sym, include_all)
|
|
19
|
+
|
|
20
|
+
def method_missing(sym, *args, &block)
|
|
21
|
+
res = @target.send(sym, *args, &block)
|
|
22
|
+
@calls << ({ sym:, args:, res:})
|
|
23
|
+
res
|
|
24
|
+
rescue Exception => e
|
|
25
|
+
@calls << ({ sym:, args:, res: e})
|
|
26
|
+
raise
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def last_call
|
|
30
|
+
calls.last
|
|
31
|
+
end
|
|
32
|
+
end
|
|
5
33
|
|
|
6
34
|
class FiberSchedulerTest < UMBaseTest
|
|
7
35
|
def setup
|
|
8
36
|
super
|
|
9
|
-
@
|
|
37
|
+
@raw_scheduler = UM::FiberScheduler.new(@machine)
|
|
38
|
+
@scheduler = MethodCallAuditor.new(@raw_scheduler)
|
|
10
39
|
Fiber.set_scheduler(@scheduler)
|
|
11
40
|
end
|
|
12
41
|
|
|
13
42
|
def teardown
|
|
14
43
|
Fiber.set_scheduler(nil)
|
|
15
|
-
|
|
44
|
+
super
|
|
16
45
|
end
|
|
17
46
|
|
|
18
47
|
def test_fiber_scheduler_initialize_without_machine
|
|
@@ -20,16 +49,6 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
20
49
|
assert_kind_of UringMachine, s.machine
|
|
21
50
|
end
|
|
22
51
|
|
|
23
|
-
def test_fiber_scheduler_post_fork
|
|
24
|
-
Fiber.schedule {}
|
|
25
|
-
assert_equal 1, @scheduler.fiber_map.size
|
|
26
|
-
|
|
27
|
-
machine_before = @scheduler.machine
|
|
28
|
-
@scheduler.post_fork
|
|
29
|
-
refute_equal machine_before, @scheduler.machine
|
|
30
|
-
assert_equal 0, @scheduler.fiber_map.size
|
|
31
|
-
end
|
|
32
|
-
|
|
33
52
|
def test_fiber_scheduler_spinning
|
|
34
53
|
f1 = Fiber.schedule do
|
|
35
54
|
sleep 0.001
|
|
@@ -41,26 +60,30 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
41
60
|
|
|
42
61
|
assert_kind_of Fiber, f1
|
|
43
62
|
assert_kind_of Fiber, f2
|
|
63
|
+
|
|
64
|
+
assert_equal 2, @scheduler.calls.size
|
|
65
|
+
assert_equal [:fiber] * 2, @scheduler.calls.map { it[:sym] }
|
|
44
66
|
assert_equal 2, @scheduler.fiber_map.size
|
|
45
67
|
|
|
46
68
|
# close scheduler
|
|
47
69
|
Fiber.set_scheduler nil
|
|
70
|
+
assert_equal :scheduler_close, @scheduler.last_call[:sym]
|
|
48
71
|
GC.start
|
|
49
72
|
assert_equal 0, @scheduler.fiber_map.size
|
|
50
73
|
end
|
|
51
74
|
|
|
52
|
-
def
|
|
75
|
+
def test_fiber_scheduler_io_read_io_write
|
|
53
76
|
i, o = IO.pipe
|
|
54
77
|
buffer = []
|
|
55
78
|
|
|
56
79
|
f1 = Fiber.schedule do
|
|
57
|
-
sleep 0.
|
|
80
|
+
sleep 0.01
|
|
58
81
|
o.write 'foo'
|
|
59
82
|
buffer << :f1
|
|
60
83
|
end
|
|
61
84
|
|
|
62
85
|
f2 = Fiber.schedule do
|
|
63
|
-
sleep 0.
|
|
86
|
+
sleep 0.02
|
|
64
87
|
o.write 'bar'
|
|
65
88
|
buffer << :f2
|
|
66
89
|
o.close
|
|
@@ -74,27 +97,163 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
74
97
|
@scheduler.join
|
|
75
98
|
assert_equal [true] * 3, [f1, f2, f3].map(&:done?)
|
|
76
99
|
assert_equal [:f1, :f2, 'foobar'], buffer
|
|
100
|
+
|
|
101
|
+
assert_equal({
|
|
102
|
+
fiber: 3,
|
|
103
|
+
kernel_sleep: 2,
|
|
104
|
+
io_write: 2,
|
|
105
|
+
io_read: 3,
|
|
106
|
+
io_close: 1,
|
|
107
|
+
join: 1
|
|
108
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
109
|
+
ensure
|
|
110
|
+
i.close rescue nil
|
|
111
|
+
o.close rescue nil
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_io_read_with_timeout
|
|
115
|
+
i, o = IO.pipe
|
|
116
|
+
i.timeout = 0.01
|
|
117
|
+
buf = []
|
|
118
|
+
|
|
119
|
+
Fiber.schedule do
|
|
120
|
+
buf << i.read
|
|
121
|
+
rescue Timeout::Error
|
|
122
|
+
buf << :timeout
|
|
123
|
+
end
|
|
124
|
+
@scheduler.join
|
|
125
|
+
assert_equal [:timeout], buf
|
|
126
|
+
|
|
127
|
+
assert_equal({
|
|
128
|
+
fiber: 1,
|
|
129
|
+
io_read: 1,
|
|
130
|
+
join: 1
|
|
131
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
132
|
+
ensure
|
|
133
|
+
i.close rescue nil
|
|
134
|
+
o.close rescue nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_io_write_with_timeout
|
|
138
|
+
i, o = IO.pipe
|
|
139
|
+
o << ('*' * (1 << 16))
|
|
140
|
+
o.timeout = 0.01
|
|
141
|
+
|
|
142
|
+
buf = []
|
|
143
|
+
|
|
144
|
+
Fiber.schedule do
|
|
145
|
+
buf << o.write('!')
|
|
146
|
+
rescue Timeout::Error
|
|
147
|
+
buf << :timeout
|
|
148
|
+
end
|
|
149
|
+
@scheduler.join
|
|
150
|
+
assert_equal [:timeout], buf
|
|
151
|
+
|
|
152
|
+
assert_equal({
|
|
153
|
+
fiber: 1,
|
|
154
|
+
io_write: 1,
|
|
155
|
+
join: 1
|
|
156
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
77
157
|
ensure
|
|
78
158
|
i.close rescue nil
|
|
79
159
|
o.close rescue nil
|
|
80
160
|
end
|
|
81
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
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_fiber_io_pread
|
|
184
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
185
|
+
IO.write(fn, 'foobar')
|
|
186
|
+
|
|
187
|
+
buf = nil
|
|
188
|
+
Fiber.schedule do
|
|
189
|
+
File.open(fn, 'r') do |f|
|
|
190
|
+
buf = f.pread(3, 2)
|
|
191
|
+
end
|
|
192
|
+
rescue => e
|
|
193
|
+
buf = e
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
@scheduler.join
|
|
197
|
+
assert_equal 'oba', buf
|
|
198
|
+
assert_equal({
|
|
199
|
+
fiber: 1,
|
|
200
|
+
blocking_operation_wait: 1,
|
|
201
|
+
io_pread: 1,
|
|
202
|
+
io_close: 1,
|
|
203
|
+
join: 1
|
|
204
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
205
|
+
ensure
|
|
206
|
+
FileUtils.rm(fn) rescue nil
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def test_fiber_scheduler_io_pwrite
|
|
210
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
211
|
+
IO.write(fn, 'foobar')
|
|
212
|
+
|
|
213
|
+
res = nil
|
|
214
|
+
Fiber.schedule do
|
|
215
|
+
File.open(fn, 'r+') do |f|
|
|
216
|
+
res = f.pwrite('baz', 2)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
@scheduler.join
|
|
221
|
+
assert_equal 3, res
|
|
222
|
+
|
|
223
|
+
assert_equal 'fobazr', IO.read(fn)
|
|
224
|
+
assert_equal({
|
|
225
|
+
fiber: 1,
|
|
226
|
+
blocking_operation_wait: 1,
|
|
227
|
+
io_pwrite: 1,
|
|
228
|
+
io_close: 1,
|
|
229
|
+
join: 1
|
|
230
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
231
|
+
ensure
|
|
232
|
+
FileUtils.rm(fn) rescue nil
|
|
233
|
+
end
|
|
234
|
+
|
|
82
235
|
def test_fiber_scheduler_sleep
|
|
83
236
|
t0 = monotonic_clock
|
|
84
|
-
assert_equal 0, machine.
|
|
237
|
+
assert_equal 0, machine.metrics[:ops_pending]
|
|
85
238
|
Fiber.schedule do
|
|
86
239
|
sleep(0.01)
|
|
87
240
|
end
|
|
88
241
|
Fiber.schedule do
|
|
89
242
|
sleep(0.02)
|
|
90
243
|
end
|
|
91
|
-
assert_equal 2, machine.
|
|
244
|
+
assert_equal 2, machine.metrics[:ops_pending]
|
|
92
245
|
@scheduler.join
|
|
93
246
|
t1 = monotonic_clock
|
|
94
247
|
assert_in_range 0.02..0.025, t1 - t0
|
|
248
|
+
|
|
249
|
+
assert_equal({
|
|
250
|
+
fiber: 2,
|
|
251
|
+
kernel_sleep: 2,
|
|
252
|
+
join: 1
|
|
253
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
95
254
|
end
|
|
96
255
|
|
|
97
|
-
def
|
|
256
|
+
def test_fiber_scheduler_block
|
|
98
257
|
mutex = Mutex.new
|
|
99
258
|
buffer = []
|
|
100
259
|
t0 = monotonic_clock
|
|
@@ -109,15 +268,25 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
109
268
|
end
|
|
110
269
|
@scheduler.join
|
|
111
270
|
t1 = monotonic_clock
|
|
112
|
-
assert_in_range 0.01..0.
|
|
271
|
+
assert_in_range 0.01..0.020, t1 - t0
|
|
272
|
+
assert_equal({
|
|
273
|
+
fiber: 3,
|
|
274
|
+
kernel_sleep: 12,
|
|
275
|
+
block: 1,
|
|
276
|
+
unblock: 1,
|
|
277
|
+
join: 1
|
|
278
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
113
279
|
end
|
|
114
280
|
|
|
115
281
|
def test_fiber_scheduler_process_wait
|
|
282
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
283
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
284
|
+
|
|
116
285
|
child_pid = nil
|
|
117
286
|
status = nil
|
|
118
287
|
f1 = Fiber.schedule do
|
|
119
288
|
child_pid = fork {
|
|
120
|
-
Fiber.scheduler.
|
|
289
|
+
Fiber.scheduler.process_fork
|
|
121
290
|
Fiber.set_scheduler nil
|
|
122
291
|
sleep(0.01);
|
|
123
292
|
exit! 42
|
|
@@ -130,9 +299,1245 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
130
299
|
assert_kind_of Process::Status, status
|
|
131
300
|
assert_equal child_pid, status.pid
|
|
132
301
|
assert_equal 42, status.exitstatus
|
|
302
|
+
assert_equal({
|
|
303
|
+
fiber: 1,
|
|
304
|
+
process_wait: 1,
|
|
305
|
+
join: 1
|
|
306
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
133
307
|
ensure
|
|
134
308
|
if child_pid
|
|
135
309
|
Process.wait(child_pid) rescue nil
|
|
136
310
|
end
|
|
137
311
|
end
|
|
312
|
+
|
|
313
|
+
# Currently the fiber scheduler doesn't have hooks for send/recv. The only
|
|
314
|
+
# hook that will be invoked is `io_wait`.
|
|
315
|
+
def test_fiber_scheduler_sockets
|
|
316
|
+
s1, s2 = UNIXSocket.pair(:STREAM)
|
|
317
|
+
|
|
318
|
+
buf = +''
|
|
319
|
+
sent = nil
|
|
320
|
+
|
|
321
|
+
assert_equal 0, machine.metrics[:total_ops]
|
|
322
|
+
Fiber.schedule do
|
|
323
|
+
buf = s1.recv(12)
|
|
324
|
+
end
|
|
325
|
+
Fiber.schedule do
|
|
326
|
+
sent = s2.send('foobar', 0)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# In Ruby, sockets are by default non-blocking. The recv will cause io_wait
|
|
330
|
+
# to be invoked, the send should get through without needing to poll.
|
|
331
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
332
|
+
@scheduler.join
|
|
333
|
+
|
|
334
|
+
assert_equal 6, sent
|
|
335
|
+
assert_equal 'foobar', buf
|
|
336
|
+
assert_equal({
|
|
337
|
+
fiber: 2,
|
|
338
|
+
io_wait: 1,
|
|
339
|
+
join: 1
|
|
340
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
341
|
+
ensure
|
|
342
|
+
s1.close rescue nil
|
|
343
|
+
s2.close rescue nil
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def test_fiber_scheduler_io_write_io_read
|
|
347
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
348
|
+
Fiber.schedule do
|
|
349
|
+
IO.write(fn, 'foobar')
|
|
350
|
+
end
|
|
351
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
352
|
+
|
|
353
|
+
buf = nil
|
|
354
|
+
Fiber.schedule do
|
|
355
|
+
sleep 0.001
|
|
356
|
+
buf = IO.read(fn)
|
|
357
|
+
end
|
|
358
|
+
assert_equal 2, machine.metrics[:total_ops]
|
|
359
|
+
|
|
360
|
+
@scheduler.join
|
|
361
|
+
assert_equal 'foobar', buf
|
|
362
|
+
assert_equal({
|
|
363
|
+
fiber: 2,
|
|
364
|
+
blocking_operation_wait: 2,
|
|
365
|
+
io_read: 2,
|
|
366
|
+
io_close: 2,
|
|
367
|
+
kernel_sleep: 1,
|
|
368
|
+
join: 1
|
|
369
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
370
|
+
ensure
|
|
371
|
+
FileUtils.rm(fn) rescue nil
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def test_fiber_scheduler_file_io
|
|
375
|
+
fn = "/tmp/um_#{SecureRandom.hex}"
|
|
376
|
+
Fiber.schedule do
|
|
377
|
+
File.open(fn, 'w') { it.write 'foobar' }
|
|
378
|
+
end
|
|
379
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
380
|
+
|
|
381
|
+
buf = nil
|
|
382
|
+
Fiber.schedule do
|
|
383
|
+
sleep 0.001
|
|
384
|
+
File.open(fn, 'r') { buf = it.read }
|
|
385
|
+
end
|
|
386
|
+
assert_equal 2, machine.metrics[:total_ops]
|
|
387
|
+
@scheduler.join
|
|
388
|
+
assert_equal 'foobar', buf
|
|
389
|
+
assert_equal({
|
|
390
|
+
fiber: 2,
|
|
391
|
+
blocking_operation_wait: 2,
|
|
392
|
+
io_read: 2,
|
|
393
|
+
io_close: 2,
|
|
394
|
+
kernel_sleep: 1,
|
|
395
|
+
join: 1
|
|
396
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
397
|
+
ensure
|
|
398
|
+
FileUtils.rm(fn) rescue nil
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def test_fiber_scheduler_mutex
|
|
402
|
+
mutex = Mutex.new
|
|
403
|
+
|
|
404
|
+
buf = []
|
|
405
|
+
Fiber.schedule do
|
|
406
|
+
buf << 11
|
|
407
|
+
mutex.synchronize {
|
|
408
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
409
|
+
sleep 0.01
|
|
410
|
+
buf << [13, machine.metrics[:total_ops]]
|
|
411
|
+
}
|
|
412
|
+
buf << 14
|
|
413
|
+
end
|
|
414
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
415
|
+
|
|
416
|
+
Fiber.schedule do
|
|
417
|
+
buf << 21
|
|
418
|
+
mutex.synchronize {
|
|
419
|
+
buf << [22, machine.metrics[:total_ops]]
|
|
420
|
+
sleep 0.01
|
|
421
|
+
buf << [23, machine.metrics[:total_ops]]
|
|
422
|
+
}
|
|
423
|
+
buf << 24
|
|
424
|
+
end
|
|
425
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
426
|
+
|
|
427
|
+
@scheduler.join
|
|
428
|
+
assert_equal [11, [12, 0], 21, [13, 2], 14, [22, 2], [23, 4], 24], buf
|
|
429
|
+
assert_equal({
|
|
430
|
+
fiber: 2,
|
|
431
|
+
kernel_sleep: 2,
|
|
432
|
+
block: 1,
|
|
433
|
+
unblock: 1,
|
|
434
|
+
join: 1
|
|
435
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def test_fiber_scheduler_queue_shift
|
|
439
|
+
queue = Queue.new
|
|
440
|
+
|
|
441
|
+
buf = []
|
|
442
|
+
Fiber.schedule do
|
|
443
|
+
buf << [11, machine.metrics[:total_ops]]
|
|
444
|
+
buf << queue.shift
|
|
445
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
446
|
+
end
|
|
447
|
+
Fiber.schedule do
|
|
448
|
+
buf << [21, machine.metrics[:total_ops]]
|
|
449
|
+
queue << :foo
|
|
450
|
+
buf << [22, machine.metrics[:total_ops]]
|
|
451
|
+
end
|
|
452
|
+
assert_equal 0, machine.metrics[:total_ops]
|
|
453
|
+
@scheduler.join
|
|
454
|
+
|
|
455
|
+
assert_equal [[11, 0], [21, 0], [22, 0], :foo, [12, 1]], buf
|
|
456
|
+
assert_equal({
|
|
457
|
+
fiber: 2,
|
|
458
|
+
block: 1,
|
|
459
|
+
unblock: 1,
|
|
460
|
+
join: 1
|
|
461
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def test_fiber_scheduler_queue_shift_with_timeout
|
|
465
|
+
queue = Queue.new
|
|
466
|
+
|
|
467
|
+
buf = []
|
|
468
|
+
Fiber.schedule do
|
|
469
|
+
buf << [11, machine.metrics[:total_ops]]
|
|
470
|
+
buf << queue.shift(timeout: 0.01)
|
|
471
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
472
|
+
end
|
|
473
|
+
Fiber.schedule do
|
|
474
|
+
buf << [21, machine.metrics[:total_ops]]
|
|
475
|
+
end
|
|
476
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
477
|
+
@scheduler.join
|
|
478
|
+
|
|
479
|
+
assert_equal [[11, 0], [21, 1], nil, [12, 2]], buf
|
|
480
|
+
assert_equal({
|
|
481
|
+
fiber: 2,
|
|
482
|
+
block: 1,
|
|
483
|
+
join: 1
|
|
484
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def test_fiber_scheduler_thread_join
|
|
488
|
+
thread = Thread.new do
|
|
489
|
+
sleep 0.1
|
|
490
|
+
end
|
|
491
|
+
Fiber.schedule do
|
|
492
|
+
thread.join
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# No ops are issued, except for a NOP SQE used to wakeup the waiting thread.
|
|
496
|
+
assert_equal 0, machine.metrics[:total_ops]
|
|
497
|
+
|
|
498
|
+
@scheduler.join
|
|
499
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
500
|
+
assert_equal({
|
|
501
|
+
fiber: 1,
|
|
502
|
+
block: 1,
|
|
503
|
+
unblock: 1,
|
|
504
|
+
join: 1
|
|
505
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def test_fiber_scheduler_system
|
|
509
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
510
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
511
|
+
|
|
512
|
+
buf = []
|
|
513
|
+
Fiber.schedule do
|
|
514
|
+
buf << system('sleep 0.01')
|
|
515
|
+
end
|
|
516
|
+
@scheduler.join
|
|
517
|
+
assert_equal [true], buf
|
|
518
|
+
assert_equal({
|
|
519
|
+
fiber: 1,
|
|
520
|
+
process_wait: 1,
|
|
521
|
+
join: 1
|
|
522
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
523
|
+
ensure
|
|
524
|
+
Process.wait(0, Process::WNOHANG) rescue nil
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def test_fiber_scheduler_cmd
|
|
528
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
529
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
530
|
+
|
|
531
|
+
buf = []
|
|
532
|
+
Fiber.schedule do
|
|
533
|
+
buf << `echo 'foo'`
|
|
534
|
+
end
|
|
535
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
536
|
+
@scheduler.join
|
|
537
|
+
assert_equal ["foo\n"], buf
|
|
538
|
+
assert_equal({
|
|
539
|
+
fiber: 1,
|
|
540
|
+
io_read: 2,
|
|
541
|
+
process_wait: 1,
|
|
542
|
+
join: 1
|
|
543
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
544
|
+
ensure
|
|
545
|
+
Process.wait(0, Process::WNOHANG) rescue nil
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def test_fiber_scheduler_popen
|
|
549
|
+
skip("Missing #process_wait hook (no rb_process_status_new)") \
|
|
550
|
+
if !@scheduler.respond_to?(:process_wait)
|
|
551
|
+
|
|
552
|
+
buf = []
|
|
553
|
+
Fiber.schedule do
|
|
554
|
+
IO.popen('ruby', 'r+') do |pipe|
|
|
555
|
+
buf << [11, machine.metrics[:total_ops]]
|
|
556
|
+
pipe.puts 'puts "bar"'
|
|
557
|
+
buf << [12, machine.metrics[:total_ops]]
|
|
558
|
+
pipe.close_write
|
|
559
|
+
buf << [13, pipe.gets.chomp, machine.metrics[:total_ops]]
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
563
|
+
@scheduler.join
|
|
564
|
+
assert_equal [[11, 0], [12, 3], [13, "bar", 5]], buf
|
|
565
|
+
assert_equal({
|
|
566
|
+
fiber: 1,
|
|
567
|
+
io_write: 2,
|
|
568
|
+
io_read: 1,
|
|
569
|
+
blocking_operation_wait: 1,
|
|
570
|
+
process_wait: 1,
|
|
571
|
+
join: 1
|
|
572
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
573
|
+
ensure
|
|
574
|
+
Process.wait(0, Process::WNOHANG) rescue nil
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def test_fiber_scheduler_fiber_interrupt
|
|
578
|
+
r, w = IO.pipe
|
|
579
|
+
w << 'foo'
|
|
580
|
+
|
|
581
|
+
exception = nil
|
|
582
|
+
Fiber.schedule do
|
|
583
|
+
r.read
|
|
584
|
+
rescue Exception => e
|
|
585
|
+
exception = e
|
|
586
|
+
end
|
|
587
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
588
|
+
machine.snooze
|
|
589
|
+
Thread.new {
|
|
590
|
+
r.close
|
|
591
|
+
}
|
|
592
|
+
@scheduler.join
|
|
593
|
+
assert_kind_of IOError, exception
|
|
594
|
+
assert_equal({
|
|
595
|
+
fiber: 1,
|
|
596
|
+
io_read: 2,
|
|
597
|
+
fiber_interrupt: 1,
|
|
598
|
+
join: 1
|
|
599
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
600
|
+
ensure
|
|
601
|
+
r.close rescue nil
|
|
602
|
+
w.close rescue nil
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def test_fiber_scheduler_address_resolve
|
|
606
|
+
addrs = nil
|
|
607
|
+
Fiber.schedule do
|
|
608
|
+
addrs = Addrinfo.getaddrinfo("localhost", 80, Socket::AF_INET, :STREAM)
|
|
609
|
+
end
|
|
610
|
+
assert_equal 1, machine.metrics[:total_ops]
|
|
611
|
+
@scheduler.join
|
|
612
|
+
assert_kind_of Array, addrs
|
|
613
|
+
addr = addrs.first
|
|
614
|
+
assert_kind_of Addrinfo, addr
|
|
615
|
+
assert_includes ['127.0.0.1', '::1'], addr.ip_address
|
|
616
|
+
assert_equal({
|
|
617
|
+
fiber: 1,
|
|
618
|
+
io_read: 2,
|
|
619
|
+
io_close: 1,
|
|
620
|
+
blocking_operation_wait: 1,
|
|
621
|
+
address_resolve: 1,
|
|
622
|
+
join: 1
|
|
623
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def test_fiber_scheduler_timeout_after
|
|
627
|
+
res = nil
|
|
628
|
+
Fiber.schedule do
|
|
629
|
+
Timeout.timeout(0.05) do
|
|
630
|
+
sleep 1
|
|
631
|
+
end
|
|
632
|
+
res = true
|
|
633
|
+
rescue => e
|
|
634
|
+
res = e
|
|
635
|
+
end
|
|
636
|
+
@scheduler.join
|
|
637
|
+
assert_equal 3, machine.metrics[:total_ops]
|
|
638
|
+
assert_kind_of Timeout::Error, res
|
|
639
|
+
assert_equal({
|
|
640
|
+
fiber: 1,
|
|
641
|
+
timeout_after: 1,
|
|
642
|
+
kernel_sleep: 1,
|
|
643
|
+
join: 1
|
|
644
|
+
}, @scheduler.calls.map { it[:sym] }.tally)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def test_fiber_scheduler_io_select
|
|
648
|
+
r, w = IO.pipe
|
|
649
|
+
buf = []
|
|
650
|
+
|
|
651
|
+
Fiber.schedule do
|
|
652
|
+
buf << IO.select([r], [], [])
|
|
653
|
+
buf << IO.select([], [w], [])
|
|
654
|
+
end
|
|
655
|
+
@machine.snooze
|
|
656
|
+
w << 'foo'
|
|
657
|
+
@machine.snooze
|
|
658
|
+
assert_equal [[[r], [], []]], buf
|
|
659
|
+
@machine.snooze
|
|
660
|
+
@scheduler.join
|
|
661
|
+
assert_equal [[[r], [], []], [[], [w], []]], buf
|
|
662
|
+
ensure
|
|
663
|
+
r.close rescue nil
|
|
664
|
+
w.close rescue nil
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def test_fiber_scheduler_blocking_operation_wait_single_issuer
|
|
668
|
+
buf = []
|
|
669
|
+
(1..10).each { |i|
|
|
670
|
+
op = -> { i * 10}
|
|
671
|
+
buf << @scheduler.blocking_operation_wait(op)
|
|
672
|
+
sleep 0.01
|
|
673
|
+
@machine.snooze
|
|
674
|
+
}
|
|
675
|
+
assert_equal (1..10).map { it * 10 }, buf
|
|
676
|
+
|
|
677
|
+
buf = []
|
|
678
|
+
(1..20).each { |i|
|
|
679
|
+
op = -> { i * 10}
|
|
680
|
+
Fiber.schedule do
|
|
681
|
+
sleep 0.001
|
|
682
|
+
buf << @scheduler.blocking_operation_wait(op)
|
|
683
|
+
sleep 0.001
|
|
684
|
+
end
|
|
685
|
+
}
|
|
686
|
+
@scheduler.join
|
|
687
|
+
|
|
688
|
+
assert_equal (1..20).map { it * 10 }, buf.sort
|
|
689
|
+
end
|
|
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
|
|
138
1543
|
end
|