polyphony 0.54.0 → 0.59
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/.gitignore +3 -1
- data/CHANGELOG.md +54 -25
- data/Gemfile.lock +1 -1
- data/TODO.md +0 -3
- data/examples/core/idle_gc.rb +21 -0
- data/examples/io/pipe.rb +11 -0
- data/examples/io/splice_chunks.rb +29 -0
- data/examples/io/stdio.rb +8 -0
- data/ext/polyphony/backend_common.c +288 -0
- data/ext/polyphony/backend_common.h +49 -130
- data/ext/polyphony/backend_io_uring.c +439 -122
- data/ext/polyphony/backend_io_uring_context.c +14 -3
- data/ext/polyphony/backend_io_uring_context.h +11 -11
- data/ext/polyphony/backend_libev.c +463 -94
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/polyphony.c +17 -22
- data/ext/polyphony/polyphony.h +9 -16
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/runqueue.c +35 -72
- data/ext/polyphony/runqueue.h +27 -0
- data/ext/polyphony/thread.c +10 -84
- data/lib/polyphony/extensions/fiber.rb +2 -2
- data/lib/polyphony/extensions/socket.rb +6 -20
- data/lib/polyphony/extensions/thread.rb +8 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +3 -3
- data/test/test_backend.rb +137 -2
- data/test/test_fiber.rb +0 -1
- data/test/test_io.rb +6 -3
- data/test/test_signal.rb +1 -1
- data/test/test_thread.rb +57 -11
- data/test/test_thread_pool.rb +1 -1
- data/test/test_timer.rb +16 -10
- data/test/test_trace.rb +27 -49
- metadata +8 -4
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
|
@@ -262,7 +262,7 @@ module Polyphony
|
|
|
262
262
|
@parent = parent
|
|
263
263
|
@caller = caller
|
|
264
264
|
@block = block
|
|
265
|
-
|
|
265
|
+
Thread.backend.trace(:fiber_create, self)
|
|
266
266
|
schedule
|
|
267
267
|
end
|
|
268
268
|
|
|
@@ -311,7 +311,7 @@ module Polyphony
|
|
|
311
311
|
|
|
312
312
|
def finalize(result, uncaught_exception = false)
|
|
313
313
|
result, uncaught_exception = finalize_children(result, uncaught_exception)
|
|
314
|
-
|
|
314
|
+
Thread.backend.trace(:fiber_terminate, self, result)
|
|
315
315
|
@result = result
|
|
316
316
|
@running = false
|
|
317
317
|
inform_dependants(result, uncaught_exception)
|
|
@@ -36,9 +36,9 @@ class ::Socket
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def recvfrom(maxlen, flags = 0)
|
|
39
|
-
|
|
39
|
+
buf = +''
|
|
40
40
|
while true
|
|
41
|
-
result = recvfrom_nonblock(maxlen, flags,
|
|
41
|
+
result = recvfrom_nonblock(maxlen, flags, buf, **NO_EXCEPTION)
|
|
42
42
|
case result
|
|
43
43
|
when nil then raise IOError
|
|
44
44
|
when :wait_readable then Polyphony.backend_wait_io(self, false)
|
|
@@ -165,17 +165,10 @@ class ::TCPSocket
|
|
|
165
165
|
# Polyphony.backend_send(self, mesg, 0)
|
|
166
166
|
# end
|
|
167
167
|
|
|
168
|
-
def readpartial(maxlen, str =
|
|
169
|
-
|
|
170
|
-
result = Polyphony.backend_recv(self, @read_buffer, maxlen)
|
|
168
|
+
def readpartial(maxlen, str = +'')
|
|
169
|
+
result = Polyphony.backend_recv(self, str, maxlen)
|
|
171
170
|
raise EOFError unless result
|
|
172
171
|
|
|
173
|
-
if str
|
|
174
|
-
str << @read_buffer
|
|
175
|
-
else
|
|
176
|
-
str = @read_buffer
|
|
177
|
-
end
|
|
178
|
-
@read_buffer = +''
|
|
179
172
|
str
|
|
180
173
|
end
|
|
181
174
|
|
|
@@ -249,17 +242,10 @@ class ::UNIXSocket
|
|
|
249
242
|
Polyphony.backend_send(self, mesg, 0)
|
|
250
243
|
end
|
|
251
244
|
|
|
252
|
-
def readpartial(maxlen, str =
|
|
253
|
-
|
|
254
|
-
result = Polyphony.backend_recv(self, @read_buffer, maxlen)
|
|
245
|
+
def readpartial(maxlen, str = +'')
|
|
246
|
+
result = Polyphony.backend_recv(self, str, maxlen)
|
|
255
247
|
raise EOFError unless result
|
|
256
248
|
|
|
257
|
-
if str
|
|
258
|
-
str << @read_buffer
|
|
259
|
-
else
|
|
260
|
-
str = @read_buffer
|
|
261
|
-
end
|
|
262
|
-
@read_buffer = +''
|
|
263
249
|
str
|
|
264
250
|
end
|
|
265
251
|
|
data/lib/polyphony/version.rb
CHANGED
data/test/helper.rb
CHANGED
|
@@ -15,9 +15,9 @@ require 'minitest/reporters'
|
|
|
15
15
|
|
|
16
16
|
::Exception.__disable_sanitized_backtrace__ = true
|
|
17
17
|
|
|
18
|
-
Minitest::Reporters.use! [
|
|
19
|
-
|
|
20
|
-
]
|
|
18
|
+
# Minitest::Reporters.use! [
|
|
19
|
+
# Minitest::Reporters::SpecReporter.new
|
|
20
|
+
# ]
|
|
21
21
|
|
|
22
22
|
class ::Fiber
|
|
23
23
|
attr_writer :auto_watcher
|
data/test/test_backend.rb
CHANGED
|
@@ -26,7 +26,7 @@ class BackendTest < MiniTest::Test
|
|
|
26
26
|
@backend.sleep 0.01
|
|
27
27
|
count += 1
|
|
28
28
|
}.await
|
|
29
|
-
assert_in_delta 0.03, Time.now - t0, 0.
|
|
29
|
+
assert_in_delta 0.03, Time.now - t0, 0.01
|
|
30
30
|
assert_equal 3, count
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -281,6 +281,97 @@ class BackendTest < MiniTest::Test
|
|
|
281
281
|
f.await
|
|
282
282
|
end
|
|
283
283
|
end
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_splice_chunks
|
|
287
|
+
body = 'abcd' * 250
|
|
288
|
+
chunk_size = 750
|
|
289
|
+
|
|
290
|
+
buf = +''
|
|
291
|
+
r, w = IO.pipe
|
|
292
|
+
reader = spin do
|
|
293
|
+
r.read_loop { |data| buf << data }
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
i, o = IO.pipe
|
|
297
|
+
writer = spin do
|
|
298
|
+
o << body
|
|
299
|
+
o.close
|
|
300
|
+
end
|
|
301
|
+
Thread.current.backend.splice_chunks(
|
|
302
|
+
i,
|
|
303
|
+
w,
|
|
304
|
+
"Content-Type: foo\r\n\r\n",
|
|
305
|
+
"0\r\n\r\n",
|
|
306
|
+
->(len) { "#{len.to_s(16)}\r\n" },
|
|
307
|
+
"\r\n",
|
|
308
|
+
chunk_size
|
|
309
|
+
)
|
|
310
|
+
w.close
|
|
311
|
+
reader.await
|
|
312
|
+
|
|
313
|
+
expected = "Content-Type: foo\r\n\r\n#{750.to_s(16)}\r\n#{body[0..749]}\r\n#{250.to_s(16)}\r\n#{body[750..999]}\r\n0\r\n\r\n"
|
|
314
|
+
assert_equal expected, buf
|
|
315
|
+
ensure
|
|
316
|
+
o.close
|
|
317
|
+
w.close
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def test_idle_gc
|
|
321
|
+
GC.disable
|
|
322
|
+
|
|
323
|
+
count = GC.count
|
|
324
|
+
snooze
|
|
325
|
+
assert_equal count, GC.count
|
|
326
|
+
sleep 0.01
|
|
327
|
+
assert_equal count, GC.count
|
|
328
|
+
|
|
329
|
+
@backend.idle_gc_period = 0.1
|
|
330
|
+
snooze
|
|
331
|
+
assert_equal count, GC.count
|
|
332
|
+
sleep 0.05
|
|
333
|
+
assert_equal count, GC.count
|
|
334
|
+
# The idle tasks are ran at most once per fiber switch, before the backend
|
|
335
|
+
# is polled. Therefore, the second sleep will not have triggered a GC, since
|
|
336
|
+
# only 0.05s have passed since the gc period was set.
|
|
337
|
+
sleep 0.07
|
|
338
|
+
assert_equal count, GC.count
|
|
339
|
+
# Upon the third sleep the GC should be triggered, at 0.12s post setting the
|
|
340
|
+
# GC period.
|
|
341
|
+
sleep 0.05
|
|
342
|
+
assert_equal count + 1, GC.count
|
|
343
|
+
|
|
344
|
+
@backend.idle_gc_period = 0
|
|
345
|
+
count = GC.count
|
|
346
|
+
sleep 0.001
|
|
347
|
+
sleep 0.002
|
|
348
|
+
sleep 0.003
|
|
349
|
+
assert_equal count, GC.count
|
|
350
|
+
ensure
|
|
351
|
+
GC.enable
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def test_idle_proc
|
|
355
|
+
counter = 0
|
|
356
|
+
|
|
357
|
+
@backend.idle_proc = proc { counter += 1 }
|
|
358
|
+
|
|
359
|
+
3.times { snooze }
|
|
360
|
+
assert_equal 0, counter
|
|
361
|
+
|
|
362
|
+
sleep 0.01
|
|
363
|
+
assert_equal 1, counter
|
|
364
|
+
sleep 0.01
|
|
365
|
+
assert_equal 2, counter
|
|
366
|
+
|
|
367
|
+
assert_equal 2, counter
|
|
368
|
+
3.times { snooze }
|
|
369
|
+
assert_equal 2, counter
|
|
370
|
+
|
|
371
|
+
@backend.idle_proc = nil
|
|
372
|
+
sleep 0.01
|
|
373
|
+
assert_equal 2, counter
|
|
374
|
+
end
|
|
284
375
|
end
|
|
285
376
|
|
|
286
377
|
class BackendChainTest < MiniTest::Test
|
|
@@ -309,6 +400,36 @@ class BackendChainTest < MiniTest::Test
|
|
|
309
400
|
assert_equal 'hello world', i.read
|
|
310
401
|
end
|
|
311
402
|
|
|
403
|
+
def test_simple_send_chain
|
|
404
|
+
port = rand(1234..5678)
|
|
405
|
+
server = TCPServer.new('127.0.0.1', port)
|
|
406
|
+
|
|
407
|
+
server_fiber = spin do
|
|
408
|
+
while (socket = server.accept)
|
|
409
|
+
spin do
|
|
410
|
+
while (data = socket.gets(8192))
|
|
411
|
+
socket << data
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
snooze
|
|
418
|
+
client = TCPSocket.new('127.0.0.1', port)
|
|
419
|
+
|
|
420
|
+
result = Thread.backend.chain(
|
|
421
|
+
[:send, client, 'hello', 0],
|
|
422
|
+
[:send, client, " world\n", 0]
|
|
423
|
+
)
|
|
424
|
+
sleep 0.1
|
|
425
|
+
assert_equal "hello world\n", client.recv(8192)
|
|
426
|
+
client.close
|
|
427
|
+
ensure
|
|
428
|
+
server_fiber&.stop
|
|
429
|
+
server_fiber&.await
|
|
430
|
+
server&.close
|
|
431
|
+
end
|
|
432
|
+
|
|
312
433
|
def chunk_header(len)
|
|
313
434
|
"Content-Length: #{len}\r\n\r\n"
|
|
314
435
|
end
|
|
@@ -346,7 +467,16 @@ class BackendChainTest < MiniTest::Test
|
|
|
346
467
|
|
|
347
468
|
assert_raises(RuntimeError) {
|
|
348
469
|
Thread.backend.chain(
|
|
349
|
-
[:read,
|
|
470
|
+
[:read, i]
|
|
471
|
+
)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
assert_raises(RuntimeError) {
|
|
475
|
+
Thread.backend.chain(
|
|
476
|
+
[:write, o, 'abc'],
|
|
477
|
+
[:write, o, 'abc'],
|
|
478
|
+
[:write, o, 'abc'],
|
|
479
|
+
[:read, i]
|
|
350
480
|
)
|
|
351
481
|
}
|
|
352
482
|
|
|
@@ -355,5 +485,10 @@ class BackendChainTest < MiniTest::Test
|
|
|
355
485
|
[:write, o]
|
|
356
486
|
)
|
|
357
487
|
}
|
|
488
|
+
|
|
489
|
+
# Eventually we should add some APIs to the io_uring backend to query the
|
|
490
|
+
# contxt store, then add some tests here to verify that the chain op ctx is
|
|
491
|
+
# released properly before raising the error (for the time being this has
|
|
492
|
+
# been verified manually).
|
|
358
493
|
end
|
|
359
494
|
end
|
data/test/test_fiber.rb
CHANGED
data/test/test_io.rb
CHANGED
|
@@ -98,8 +98,10 @@ class IOTest < MiniTest::Test
|
|
|
98
98
|
|
|
99
99
|
buf = []
|
|
100
100
|
f = spin do
|
|
101
|
+
peer = receive
|
|
101
102
|
while (l = i.gets)
|
|
102
103
|
buf << l
|
|
104
|
+
peer << true
|
|
103
105
|
end
|
|
104
106
|
end
|
|
105
107
|
|
|
@@ -107,11 +109,12 @@ class IOTest < MiniTest::Test
|
|
|
107
109
|
assert_equal [], buf
|
|
108
110
|
|
|
109
111
|
o << 'fab'
|
|
110
|
-
|
|
112
|
+
f << Fiber.current
|
|
113
|
+
sleep 0.05
|
|
111
114
|
assert_equal [], buf
|
|
112
115
|
|
|
113
116
|
o << "ulous\n"
|
|
114
|
-
|
|
117
|
+
receive
|
|
115
118
|
assert_equal ["fabulous\n"], buf
|
|
116
119
|
|
|
117
120
|
o.close
|
|
@@ -287,7 +290,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
|
287
290
|
end
|
|
288
291
|
|
|
289
292
|
def test_foreach
|
|
290
|
-
skip
|
|
293
|
+
skip 'IO.foreach is not yet implemented'
|
|
291
294
|
lines = []
|
|
292
295
|
IO.foreach(__FILE__) { |l| lines << l }
|
|
293
296
|
assert_equal "# frozen_string_literal: true\n", lines[0]
|
data/test/test_signal.rb
CHANGED
data/test/test_thread.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'helper'
|
|
4
|
-
require 'polyphony/adapters/trace'
|
|
5
4
|
|
|
6
5
|
class ThreadTest < MiniTest::Test
|
|
7
6
|
def test_thread_spin
|
|
@@ -130,18 +129,13 @@ class ThreadTest < MiniTest::Test
|
|
|
130
129
|
|
|
131
130
|
def test_that_suspend_returns_immediately_if_no_watchers
|
|
132
131
|
records = []
|
|
133
|
-
|
|
134
|
-
records << r if r[:event] =~ /^fiber_/
|
|
135
|
-
end
|
|
136
|
-
t.enable
|
|
137
|
-
Polyphony.trace(true)
|
|
138
|
-
|
|
132
|
+
Thread.backend.trace_proc = proc {|*r| records << r }
|
|
139
133
|
suspend
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
assert_equal [
|
|
135
|
+
[:fiber_switchpoint, Fiber.current]
|
|
136
|
+
], records
|
|
142
137
|
ensure
|
|
143
|
-
|
|
144
|
-
Polyphony.trace(false)
|
|
138
|
+
Thread.backend.trace_proc = nil
|
|
145
139
|
end
|
|
146
140
|
|
|
147
141
|
def test_thread_child_fiber_termination
|
|
@@ -171,4 +165,56 @@ class ThreadTest < MiniTest::Test
|
|
|
171
165
|
t&.kill
|
|
172
166
|
t&.join
|
|
173
167
|
end
|
|
168
|
+
|
|
169
|
+
def test_idle_gc
|
|
170
|
+
GC.disable
|
|
171
|
+
|
|
172
|
+
count = GC.count
|
|
173
|
+
snooze
|
|
174
|
+
assert_equal count, GC.count
|
|
175
|
+
sleep 0.01
|
|
176
|
+
assert_equal count, GC.count
|
|
177
|
+
|
|
178
|
+
Thread.current.idle_gc_period = 0.1
|
|
179
|
+
snooze
|
|
180
|
+
assert_equal count, GC.count
|
|
181
|
+
sleep 0.05
|
|
182
|
+
assert_equal count, GC.count
|
|
183
|
+
# The idle tasks are ran at most once per fiber switch, before the backend
|
|
184
|
+
# is polled. Therefore, the second sleep will not have triggered a GC, since
|
|
185
|
+
# only 0.05s have passed since the gc period was set.
|
|
186
|
+
sleep 0.07
|
|
187
|
+
assert_equal count, GC.count
|
|
188
|
+
# Upon the third sleep the GC should be triggered, at 0.12s post setting the
|
|
189
|
+
# GC period.
|
|
190
|
+
sleep 0.05
|
|
191
|
+
assert_equal count + 1, GC.count
|
|
192
|
+
|
|
193
|
+
Thread.current.idle_gc_period = 0
|
|
194
|
+
count = GC.count
|
|
195
|
+
sleep 0.001
|
|
196
|
+
sleep 0.002
|
|
197
|
+
sleep 0.003
|
|
198
|
+
assert_equal count, GC.count
|
|
199
|
+
ensure
|
|
200
|
+
GC.enable
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_on_idle
|
|
204
|
+
counter = 0
|
|
205
|
+
|
|
206
|
+
Thread.current.on_idle { counter += 1 }
|
|
207
|
+
|
|
208
|
+
3.times { snooze }
|
|
209
|
+
assert_equal 0, counter
|
|
210
|
+
|
|
211
|
+
sleep 0.01
|
|
212
|
+
assert_equal 1, counter
|
|
213
|
+
sleep 0.01
|
|
214
|
+
assert_equal 2, counter
|
|
215
|
+
|
|
216
|
+
assert_equal 2, counter
|
|
217
|
+
3.times { snooze }
|
|
218
|
+
assert_equal 2, counter
|
|
219
|
+
end
|
|
174
220
|
end
|
data/test/test_thread_pool.rb
CHANGED
data/test/test_timer.rb
CHANGED
|
@@ -31,7 +31,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
|
|
|
31
31
|
end
|
|
32
32
|
t1 = Time.now
|
|
33
33
|
|
|
34
|
-
assert_in_range 0.01..0.
|
|
34
|
+
assert_in_range 0.01..0.05, t1 - t0
|
|
35
35
|
assert_equal :bar, v
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -48,7 +48,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
|
|
|
48
48
|
t1 = Time.now
|
|
49
49
|
|
|
50
50
|
assert_nil v
|
|
51
|
-
assert_in_range 0.015..0.
|
|
51
|
+
assert_in_range 0.015..0.04, t1 - t0
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -75,14 +75,21 @@ class TimerCancelAfterTest < MiniTest::Test
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def test_timer_cancel_after_with_reset
|
|
78
|
-
|
|
79
|
-
@timer.cancel_after(0.
|
|
80
|
-
sleep 0.
|
|
78
|
+
buf = []
|
|
79
|
+
@timer.cancel_after(0.13) do
|
|
80
|
+
sleep 0.05
|
|
81
|
+
buf << 1
|
|
81
82
|
@timer.reset
|
|
82
|
-
sleep 0.
|
|
83
|
+
sleep 0.05
|
|
84
|
+
buf << 2
|
|
85
|
+
@timer.reset
|
|
86
|
+
sleep 0.05
|
|
87
|
+
buf << 3
|
|
88
|
+
@timer.reset
|
|
89
|
+
sleep 0.05
|
|
90
|
+
buf << 4
|
|
83
91
|
end
|
|
84
|
-
|
|
85
|
-
assert_in_range 0.012..0.024, t1 - t0
|
|
92
|
+
assert_equal [1, 2, 3, 4], buf
|
|
86
93
|
end
|
|
87
94
|
|
|
88
95
|
class CustomException < Exception
|
|
@@ -140,7 +147,6 @@ class TimerMiscTest < MiniTest::Test
|
|
|
140
147
|
snooze
|
|
141
148
|
assert_equal [], buffer
|
|
142
149
|
sleep 0.1
|
|
143
|
-
p :post_sleep
|
|
144
150
|
assert_equal [2], buffer
|
|
145
151
|
end
|
|
146
152
|
|
|
@@ -152,6 +158,6 @@ class TimerMiscTest < MiniTest::Test
|
|
|
152
158
|
end
|
|
153
159
|
sleep 0.05
|
|
154
160
|
f.stop
|
|
155
|
-
assert_in_range
|
|
161
|
+
assert_in_range 3..7, buffer.size
|
|
156
162
|
end
|
|
157
163
|
end
|