polyphony 0.63 → 0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +19 -0
  4. data/Gemfile.lock +1 -1
  5. data/TODO.md +10 -40
  6. data/bin/pdbg +30 -0
  7. data/examples/core/await.rb +9 -1
  8. data/ext/polyphony/backend_common.c +14 -1
  9. data/ext/polyphony/backend_common.h +3 -1
  10. data/ext/polyphony/backend_io_uring.c +84 -24
  11. data/ext/polyphony/backend_io_uring_context.c +42 -0
  12. data/ext/polyphony/backend_io_uring_context.h +6 -9
  13. data/ext/polyphony/backend_libev.c +85 -39
  14. data/ext/polyphony/fiber.c +20 -0
  15. data/ext/polyphony/polyphony.c +2 -0
  16. data/ext/polyphony/polyphony.h +5 -2
  17. data/ext/polyphony/queue.c +1 -1
  18. data/ext/polyphony/runqueue.c +7 -3
  19. data/ext/polyphony/runqueue.h +4 -3
  20. data/ext/polyphony/runqueue_ring_buffer.c +25 -14
  21. data/ext/polyphony/runqueue_ring_buffer.h +2 -0
  22. data/ext/polyphony/thread.c +2 -8
  23. data/lib/polyphony.rb +6 -0
  24. data/lib/polyphony/debugger/server.rb +137 -0
  25. data/lib/polyphony/extensions/debug.rb +1 -1
  26. data/lib/polyphony/extensions/fiber.rb +40 -33
  27. data/lib/polyphony/extensions/io.rb +6 -2
  28. data/lib/polyphony/extensions/openssl.rb +8 -2
  29. data/lib/polyphony/extensions/socket.rb +15 -10
  30. data/lib/polyphony/version.rb +1 -1
  31. data/test/helper.rb +6 -4
  32. data/test/stress.rb +6 -2
  33. data/test/test_backend.rb +13 -4
  34. data/test/test_fiber.rb +33 -9
  35. data/test/test_global_api.rb +9 -4
  36. data/test/test_io.rb +2 -0
  37. data/test/test_socket.rb +14 -11
  38. data/test/test_thread.rb +3 -0
  39. data/test/test_thread_pool.rb +1 -1
  40. data/test/test_throttler.rb +2 -2
  41. data/test/test_timer.rb +5 -3
  42. metadata +4 -2
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.63'
4
+ VERSION = '0.67'
5
5
  end
data/test/helper.rb CHANGED
@@ -15,6 +15,8 @@ require 'minitest/reporters'
15
15
 
16
16
  ::Exception.__disable_sanitized_backtrace__ = true
17
17
 
18
+ IS_LINUX = RUBY_PLATFORM =~ /linux/
19
+
18
20
  # Minitest::Reporters.use! [
19
21
  # Minitest::Reporters::SpecReporter.new
20
22
  # ]
@@ -44,10 +46,6 @@ end
44
46
  class MiniTest::Test
45
47
  def setup
46
48
  # trace "* setup #{self.name}"
47
- if Fiber.current.children.size > 0
48
- puts "Children left: #{Fiber.current.children.inspect}"
49
- exit!
50
- end
51
49
  Fiber.current.setup_main_fiber
52
50
  Fiber.current.instance_variable_set(:@auto_watcher, nil)
53
51
  Thread.current.backend.finalize
@@ -58,6 +56,10 @@ class MiniTest::Test
58
56
  def teardown
59
57
  # trace "* teardown #{self.name}"
60
58
  Fiber.current.shutdown_all_children
59
+ if Fiber.current.children.size > 0
60
+ puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
61
+ exit!
62
+ end
61
63
  Fiber.current.instance_variable_set(:@auto_watcher, nil)
62
64
  rescue => e
63
65
  puts e
data/test/stress.rb CHANGED
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  count = ARGV[0] ? ARGV[0].to_i : 100
4
+ test_name = ARGV[1]
4
5
 
5
- TEST_CMD = 'ruby test/run.rb'
6
+ $test_cmd = +'ruby test/run.rb'
7
+ if test_name
8
+ $test_cmd << " --name #{test_name}"
9
+ end
6
10
 
7
11
  def run_test(count)
8
12
  puts "#{count}: running tests..."
9
13
  # sleep 1
10
- system(TEST_CMD)
14
+ system($test_cmd)
11
15
  puts
12
16
 
13
17
  return if $?.exitstatus == 0
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_range 0.02..0.04, Time.now - t0
29
+ assert_in_range 0.02..0.06, Time.now - t0 if IS_LINUX
30
30
  assert_equal 3, count
31
31
  end
32
32
 
@@ -243,6 +243,8 @@ class BackendTest < MiniTest::Test
243
243
  end
244
244
 
245
245
  def test_timer_loop
246
+ skip unless IS_LINUX
247
+
246
248
  i = 0
247
249
  f = spin do
248
250
  @backend.timer_loop(0.01) { i += 1 }
@@ -257,6 +259,8 @@ class BackendTest < MiniTest::Test
257
259
  end
258
260
 
259
261
  def test_timeout
262
+ skip unless IS_LINUX
263
+
260
264
  buffer = []
261
265
  assert_raises(Polyphony::TimeoutException) do
262
266
  @backend.timeout(0.01, Polyphony::TimeoutException) do
@@ -288,6 +292,8 @@ class BackendTest < MiniTest::Test
288
292
  end
289
293
 
290
294
  def test_nested_timeout
295
+ skip unless IS_LINUX
296
+
291
297
  buffer = []
292
298
  assert_raises(MyTimeoutException) do
293
299
  @backend.timeout(0.01, MyTimeoutException) do
@@ -347,8 +353,8 @@ class BackendTest < MiniTest::Test
347
353
 
348
354
 
349
355
  def test_splice_chunks
350
- body = 'abcd' * 250
351
- chunk_size = 750
356
+ body = 'abcd' * 4
357
+ chunk_size = 12
352
358
 
353
359
  buf = +''
354
360
  r, w = IO.pipe
@@ -373,7 +379,7 @@ class BackendTest < MiniTest::Test
373
379
  w.close
374
380
  reader.await
375
381
 
376
- 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"
382
+ expected = "Content-Type: foo\r\n\r\n#{12.to_s(16)}\r\n#{body[0..11]}\r\n#{4.to_s(16)}\r\n#{body[12..15]}\r\n0\r\n\r\n"
377
383
  assert_equal expected, buf
378
384
  ensure
379
385
  o.close
@@ -394,6 +400,9 @@ class BackendTest < MiniTest::Test
394
400
  assert_equal count, GC.count
395
401
  sleep 0.05
396
402
  assert_equal count, GC.count
403
+
404
+ return unless IS_LINUX
405
+
397
406
  # The idle tasks are ran at most once per fiber switch, before the backend
398
407
  # is polled. Therefore, the second sleep will not have triggered a GC, since
399
408
  # only 0.05s have passed since the gc period was set.
data/test/test_fiber.rb CHANGED
@@ -46,7 +46,7 @@ class FiberTest < MiniTest::Test
46
46
  def test_await_dead_children
47
47
  f1 = spin { :foo }
48
48
  f2 = spin { :bar }
49
- 2.times { snooze }
49
+ 4.times { snooze }
50
50
 
51
51
  assert_equal [:foo, :bar], Fiber.await(f1, f2)
52
52
  end
@@ -67,10 +67,12 @@ class FiberTest < MiniTest::Test
67
67
  }
68
68
  Fiber.await(f2, f3)
69
69
  assert_equal [:foo, :bar, :baz], buffer
70
- assert_equal 0, Fiber.current.children.size
70
+ assert_equal [f1], Fiber.current.children
71
+ Fiber.current.reap_dead_children
72
+ assert_equal [], Fiber.current.children
71
73
  end
72
74
 
73
- def test_await_from_multiple_fibers_with_interruption=
75
+ def test_await_from_multiple_fibers_with_interruption
74
76
  buffer = []
75
77
  f1 = spin {
76
78
  sleep 0.02
@@ -91,6 +93,8 @@ class FiberTest < MiniTest::Test
91
93
  f1.stop
92
94
 
93
95
  snooze
96
+ assert_equal [f1, f2, f3], Fiber.current.children
97
+ Fiber.current.reap_dead_children
94
98
  assert_equal [], Fiber.current.children
95
99
  end
96
100
 
@@ -563,10 +567,10 @@ class FiberTest < MiniTest::Test
563
567
  end
564
568
 
565
569
  snooze
566
- child.monitor
570
+ child.monitor(Fiber.current)
567
571
  spin { child << :foo }
568
572
 
569
- msg = receive
573
+ msg = Fiber.current.monitor_mailbox.shift
570
574
  assert_equal [child, :foo], msg
571
575
  end
572
576
 
@@ -578,14 +582,14 @@ class FiberTest < MiniTest::Test
578
582
  end
579
583
 
580
584
  snooze
581
- child.monitor
585
+ child.monitor(Fiber.current)
582
586
  spin { child << :foo }
583
587
  snooze
584
588
 
585
- child.unmonitor
589
+ child.unmonitor(Fiber.current)
586
590
 
587
- Fiber.current << :bar
588
- msg = receive
591
+ Fiber.current.monitor_mailbox << :bar
592
+ msg = Fiber.current.monitor_mailbox.shift
589
593
  assert_equal :bar, msg
590
594
  end
591
595
 
@@ -598,6 +602,7 @@ class FiberTest < MiniTest::Test
598
602
 
599
603
  f.stop
600
604
  snooze
605
+ Fiber.current.reap_dead_children
601
606
  assert_equal [], Fiber.current.children
602
607
  end
603
608
 
@@ -1218,4 +1223,23 @@ class GracefulTerminationTest < MiniTest::Test
1218
1223
 
1219
1224
  assert_equal [1, 2], buffer
1220
1225
  end
1226
+ end
1227
+
1228
+ class DebugTest < MiniTest::Test
1229
+ def test_parking
1230
+ buf = []
1231
+ f = spin do
1232
+ 3.times { |i| snooze; buf << i }
1233
+ end
1234
+ assert_nil f.__parked__?
1235
+ f.__park__
1236
+ assert_equal true, f.__parked__?
1237
+ 10.times { snooze }
1238
+ assert_equal [], buf
1239
+
1240
+ f.__unpark__
1241
+ assert_nil f.__parked__?
1242
+ 10.times { snooze }
1243
+ assert_equal [0, 1, 2], buf
1244
+ end
1221
1245
  end
@@ -137,7 +137,7 @@ class MoveOnAfterTest < MiniTest::Test
137
137
  t1 = Time.now
138
138
 
139
139
  assert_nil v
140
- assert_in_range 0.014..0.02, t1 - t0
140
+ assert_in_range 0.014..0.02, t1 - t0 if IS_LINUX
141
141
  end
142
142
 
143
143
  def test_move_on_after_without_block
@@ -152,6 +152,8 @@ class MoveOnAfterTest < MiniTest::Test
152
152
  end
153
153
 
154
154
  def test_nested_move_on_after
155
+ skip unless IS_LINUX
156
+
155
157
  t0 = Time.now
156
158
  o = move_on_after(0.01, with_value: 1) do
157
159
  move_on_after(0.02, with_value: 2) do
@@ -210,7 +212,7 @@ class CancelAfterTest < MiniTest::Test
210
212
  sleep 0.007
211
213
  end
212
214
  t1 = Time.now
213
- assert_in_range 0.014..0.024, t1 - t0
215
+ assert_in_range 0.014..0.024, t1 - t0 if IS_LINUX
214
216
  end
215
217
 
216
218
  class CustomException < Exception
@@ -373,6 +375,7 @@ class SpinScopeTest < MiniTest::Test
373
375
  buffer << e.message
374
376
  end
375
377
  10.times { snooze }
378
+ Fiber.current.reap_dead_children
376
379
  assert_equal 0, Fiber.current.children.size
377
380
  assert_equal ['foobar'], buffer
378
381
  end
@@ -399,7 +402,7 @@ class ThrottledLoopTest < MiniTest::Test
399
402
  end
400
403
  f.await
401
404
  t1 = Time.now
402
- assert_in_range 0.075..0.15, t1 - t0
405
+ assert_in_range 0.075..0.15, t1 - t0 if IS_LINUX
403
406
  assert_equal [1, 2, 3, 4, 5], buffer
404
407
  end
405
408
  end
@@ -415,6 +418,8 @@ class GlobalAPIEtcTest < MiniTest::Test
415
418
  end
416
419
 
417
420
  def test_every
421
+ skip unless IS_LINUX
422
+
418
423
  buffer = []
419
424
  t0 = Time.now
420
425
  f = spin do
@@ -429,7 +434,7 @@ class GlobalAPIEtcTest < MiniTest::Test
429
434
  t0 = Time.now
430
435
  sleep 0.1
431
436
  elapsed = Time.now - t0
432
- assert (0.05..0.15).include? elapsed
437
+ assert (0.05..0.15).include? elapsed if IS_LINUX
433
438
 
434
439
  f = spin { sleep }
435
440
  snooze
data/test/test_io.rb CHANGED
@@ -351,6 +351,8 @@ class IOClassMethodsTest < MiniTest::Test
351
351
  end
352
352
 
353
353
  def test_popen
354
+ skip unless IS_LINUX
355
+
354
356
  counter = 0
355
357
  timer = spin { throttled_loop(200) { counter += 1 } }
356
358
 
data/test/test_socket.rb CHANGED
@@ -158,19 +158,22 @@ class SocketTest < MiniTest::Test
158
158
  end
159
159
  end
160
160
 
161
- class HTTPClientTest < MiniTest::Test
162
- require 'json'
161
+ if IS_LINUX
162
+ class HTTPClientTest < MiniTest::Test
163
163
 
164
- def test_http
165
- res = HTTParty.get('http://ipinfo.io/')
164
+ require 'json'
166
165
 
167
- response = JSON.load(res.body)
168
- assert_equal 'https://ipinfo.io/missingauth', response['readme']
169
- end
166
+ def test_http
167
+ res = HTTParty.get('http://ipinfo.io/')
168
+
169
+ response = JSON.load(res.body)
170
+ assert_equal 'https://ipinfo.io/missingauth', response['readme']
171
+ end
170
172
 
171
- def test_https
172
- res = HTTParty.get('https://ipinfo.io/')
173
- response = JSON.load(res.body)
174
- assert_equal 'https://ipinfo.io/missingauth', response['readme']
173
+ def test_https
174
+ res = HTTParty.get('https://ipinfo.io/')
175
+ response = JSON.load(res.body)
176
+ assert_equal 'https://ipinfo.io/missingauth', response['readme']
177
+ end
175
178
  end
176
179
  end
data/test/test_thread.rb CHANGED
@@ -180,6 +180,9 @@ class ThreadTest < MiniTest::Test
180
180
  assert_equal count, GC.count
181
181
  sleep 0.05
182
182
  assert_equal count, GC.count
183
+
184
+ return unless IS_LINUX
185
+
183
186
  # The idle tasks are ran at most once per fiber switch, before the backend
184
187
  # is polled. Therefore, the second sleep will not have triggered a GC, since
185
188
  # only 0.05s have passed since the gc period was set.
@@ -70,7 +70,7 @@ class ThreadPoolTest < MiniTest::Test
70
70
 
71
71
  sleep 0.15 # allow time for threads to spawn
72
72
  assert_equal @pool.size, threads.uniq.size
73
- assert_equal (0..9).to_a, buffer.sort
73
+ assert_equal (0..9).to_a, buffer.sort if IS_LINUX
74
74
  end
75
75
 
76
76
  def test_busy?
@@ -10,7 +10,7 @@ class ThrottlerTest < MiniTest::Test
10
10
  f = spin { loop { t.process { buffer << 1 } } }
11
11
  sleep 0.2
12
12
  f.stop
13
- assert_in_range 1..3, buffer.size
13
+ assert_in_range 1..4, buffer.size
14
14
  ensure
15
15
  t.stop
16
16
  end
@@ -23,7 +23,7 @@ class ThrottlerTest < MiniTest::Test
23
23
  end
24
24
  sleep 0.25
25
25
  f.stop
26
- assert_in_range 2..6, buffer.size
26
+ assert_in_range 2..7, buffer.size
27
27
  ensure
28
28
  t.stop
29
29
  end
data/test/test_timer.rb CHANGED
@@ -19,7 +19,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
19
19
  end
20
20
  t1 = Time.now
21
21
 
22
- assert_in_range 0.1..0.15, t1 - t0
22
+ assert_in_range 0.1..0.15, t1 - t0 if IS_LINUX
23
23
  assert_nil v
24
24
  end
25
25
 
@@ -31,11 +31,13 @@ class TimerMoveOnAfterTest < MiniTest::Test
31
31
  end
32
32
  t1 = Time.now
33
33
 
34
- assert_in_range 0.01..0.05, t1 - t0
34
+ assert_in_range 0.01..0.05, t1 - t0 if IS_LINUX
35
35
  assert_equal :bar, v
36
36
  end
37
37
 
38
38
  def test_timer_move_on_after_with_reset
39
+ skip unless IS_LINUX
40
+
39
41
  t0 = Time.now
40
42
  v = @timer.move_on_after(0.01, with_value: :moved_on) do
41
43
  sleep 0.007
@@ -71,7 +73,7 @@ class TimerCancelAfterTest < MiniTest::Test
71
73
  end
72
74
  end
73
75
  t1 = Time.now
74
- assert_in_range 0.01..0.03, t1 - t0
76
+ assert_in_range 0.01..0.03, t1 - t0 if IS_LINUX
75
77
  end
76
78
 
77
79
  def test_timer_cancel_after_with_reset
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.63'
4
+ version: '0.67'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-26 00:00:00.000000000 Z
11
+ date: 2021-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -156,6 +156,7 @@ files:
156
156
  - README.md
157
157
  - Rakefile
158
158
  - TODO.md
159
+ - bin/pdbg
159
160
  - bin/polyphony-debug
160
161
  - bin/stress.rb
161
162
  - bin/test
@@ -356,6 +357,7 @@ files:
356
357
  - lib/polyphony/core/thread_pool.rb
357
358
  - lib/polyphony/core/throttler.rb
358
359
  - lib/polyphony/core/timer.rb
360
+ - lib/polyphony/debugger/server.rb
359
361
  - lib/polyphony/extensions/core.rb
360
362
  - lib/polyphony/extensions/debug.rb
361
363
  - lib/polyphony/extensions/fiber.rb