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.
@@ -262,7 +262,7 @@ module Polyphony
262
262
  @parent = parent
263
263
  @caller = caller
264
264
  @block = block
265
- __fiber_trace__(:fiber_create, self)
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
- __fiber_trace__(:fiber_terminate, self, result)
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
- @read_buffer ||= +''
39
+ buf = +''
40
40
  while true
41
- result = recvfrom_nonblock(maxlen, flags, @read_buffer, **NO_EXCEPTION)
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 = nil)
169
- @read_buffer ||= +''
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 = nil)
253
- @read_buffer ||= +''
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
 
@@ -105,4 +105,12 @@ class ::Thread
105
105
  main_fiber << value
106
106
  end
107
107
  alias_method :send, :<<
108
+
109
+ def idle_gc_period=(period)
110
+ backend.idle_gc_period = period
111
+ end
112
+
113
+ def on_idle(&block)
114
+ backend.idle_proc = block
115
+ end
108
116
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.54.0'
4
+ VERSION = '0.59'
5
5
  end
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
- Minitest::Reporters::SpecReporter.new
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.005
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, o]
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
@@ -132,7 +132,6 @@ class FiberTest < MiniTest::Test
132
132
  event = Polyphony::Event.new
133
133
 
134
134
  t = Thread.new do
135
- f = spin_loop { snooze }
136
135
  sleep 0.001
137
136
  event.signal(:foo)
138
137
  end
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
- snooze
112
+ f << Fiber.current
113
+ sleep 0.05
111
114
  assert_equal [], buf
112
115
 
113
116
  o << "ulous\n"
114
- sleep 0.01
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 "IO.foreach is not yet implemented"
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
@@ -50,7 +50,7 @@ class SignalTrapTest < Minitest::Test
50
50
  ensure
51
51
  o.close
52
52
  end
53
- sleep 0.02
53
+ sleep 0.1
54
54
  o.close
55
55
  Process.kill('INT', pid)
56
56
  Thread.current.backend.waitpid(pid)
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
- t = Polyphony::Trace.new(:fiber_all) do |r|
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
- t.disable
141
- assert_equal [:fiber_switchpoint], records.map { |r| r[:event] }
134
+ assert_equal [
135
+ [:fiber_switchpoint, Fiber.current]
136
+ ], records
142
137
  ensure
143
- t&.disable
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
@@ -65,7 +65,7 @@ class ThreadPoolTest < MiniTest::Test
65
65
  end
66
66
  elapsed = Time.now - t0
67
67
 
68
- assert elapsed < 0.007
68
+ assert_in_range 0.0..0.009, elapsed
69
69
  assert buffer.size < 2
70
70
 
71
71
  sleep 0.1 # allow time for threads to spawn
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.025, t1 - t0
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.03, t1 - t0
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
- t0 = Time.now
79
- @timer.cancel_after(0.01) do
80
- sleep 0.007
78
+ buf = []
79
+ @timer.cancel_after(0.13) do
80
+ sleep 0.05
81
+ buf << 1
81
82
  @timer.reset
82
- sleep 0.007
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
- t1 = Time.now
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 4..6, buffer.size
161
+ assert_in_range 3..7, buffer.size
156
162
  end
157
163
  end