polyphony 0.63 → 0.67

Sign up to get free protection for your applications and to get access to all the features.
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