polyphony 0.73.1 → 0.77

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +13 -10
  3. data/.github/workflows/test_io_uring.yml +32 -0
  4. data/CHANGELOG.md +22 -0
  5. data/Gemfile.lock +10 -7
  6. data/bin/pdbg +0 -0
  7. data/bin/polyphony-debug +0 -0
  8. data/bin/stress.rb +0 -0
  9. data/bin/test +0 -0
  10. data/examples/core/trap1.rb +21 -0
  11. data/examples/core/trap2.rb +14 -0
  12. data/ext/polyphony/backend_common.c +84 -12
  13. data/ext/polyphony/backend_common.h +8 -0
  14. data/ext/polyphony/backend_io_uring.c +231 -107
  15. data/ext/polyphony/backend_io_uring_context.c +1 -0
  16. data/ext/polyphony/backend_io_uring_context.h +2 -1
  17. data/ext/polyphony/backend_libev.c +12 -9
  18. data/ext/polyphony/event.c +5 -2
  19. data/ext/polyphony/polyphony.c +11 -1
  20. data/ext/polyphony/polyphony.h +4 -1
  21. data/ext/polyphony/queue.c +10 -5
  22. data/ext/polyphony/runqueue_ring_buffer.c +3 -1
  23. data/ext/polyphony/socket_extensions.c +5 -2
  24. data/ext/test_eintr.c +50 -0
  25. data/lib/polyphony/extensions/fiber.rb +85 -5
  26. data/lib/polyphony/extensions/openssl.rb +5 -1
  27. data/lib/polyphony/extensions/socket.rb +12 -6
  28. data/lib/polyphony/extensions/thread.rb +9 -3
  29. data/lib/polyphony/version.rb +1 -1
  30. data/lib/polyphony.rb +4 -1
  31. data/test/helper.rb +2 -6
  32. data/test/stress.rb +1 -1
  33. data/test/test_backend.rb +3 -5
  34. data/test/test_fiber.rb +6 -4
  35. data/test/test_global_api.rb +10 -14
  36. data/test/test_io.rb +2 -2
  37. data/test/test_kernel.rb +2 -2
  38. data/test/test_signal.rb +57 -0
  39. data/test/test_socket.rb +35 -2
  40. data/test/test_thread.rb +1 -1
  41. data/test/test_thread_pool.rb +1 -1
  42. data/test/test_throttler.rb +3 -3
  43. data/test/test_timer.rb +2 -2
  44. data/test/test_trace.rb +7 -1
  45. metadata +11 -7
data/test/test_backend.rb CHANGED
@@ -243,16 +243,14 @@ class BackendTest < MiniTest::Test
243
243
  end
244
244
 
245
245
  def test_timer_loop
246
- skip unless IS_LINUX
247
-
248
- i = 0
246
+ counter = 0
249
247
  f = spin do
250
- @backend.timer_loop(0.01) { i += 1 }
248
+ @backend.timer_loop(0.01) { counter += 1 }
251
249
  end
252
250
  @backend.sleep(0.05)
253
251
  f.stop
254
252
  f.await # TODO: check why this test sometimes segfaults if we don't a<wait fiber
255
- assert_in_range 4..6, i
253
+ assert_in_range 4..6, counter if IS_LINUX
256
254
  end
257
255
 
258
256
  class MyTimeoutException < Exception
data/test/test_fiber.rb CHANGED
@@ -923,18 +923,20 @@ class MailboxTest < MiniTest::Test
923
923
  def test_cross_thread_send_receive
924
924
  ping_receive_buffer = []
925
925
  pong_receive_buffer = []
926
+ master = Fiber.current
926
927
 
927
928
  pong = Thread.new do
928
- sleep 0.05
929
- loop do
929
+ master << :pong_ready
930
+ 3.times do
930
931
  peer, data = receive
931
932
  pong_receive_buffer << data
932
933
  peer << 'pong'
933
934
  end
934
935
  end
935
936
 
937
+ assert_equal :pong_ready, receive
938
+
936
939
  ping = Thread.new do
937
- sleep 0.05
938
940
  3.times do
939
941
  pong << [Fiber.current, 'ping']
940
942
  data = receive
@@ -943,7 +945,7 @@ class MailboxTest < MiniTest::Test
943
945
  end
944
946
 
945
947
  ping.join
946
- pong.kill
948
+ pong.join
947
949
  ping = pong = nil
948
950
 
949
951
  assert_equal %w{pong pong pong}, ping_receive_buffer
@@ -162,7 +162,7 @@ class MoveOnAfterTest < MiniTest::Test
162
162
  end
163
163
  t1 = Time.now
164
164
  assert_equal 1, o
165
- assert_in_range 0.008..0.015, t1 - t0
165
+ assert_in_range 0.008..0.015, t1 - t0 if IS_LINUX
166
166
 
167
167
  t0 = Time.now
168
168
  o = move_on_after(0.05, with_value: 1) do
@@ -172,7 +172,7 @@ class MoveOnAfterTest < MiniTest::Test
172
172
  end
173
173
  t1 = Time.now
174
174
  assert_equal 2, o
175
- assert_in_range 0.008..0.013, t1 - t0
175
+ assert_in_range 0.008..0.013, t1 - t0 if IS_LINUX
176
176
  end
177
177
  end
178
178
 
@@ -297,7 +297,7 @@ class SpinLoopTest < MiniTest::Test
297
297
  f = spin_loop(rate: 100) { buffer << (counter += 1) }
298
298
  sleep 0.02
299
299
  f.stop
300
- assert_in_range 1..3, counter
300
+ assert_in_range 1..3, counter if IS_LINUX
301
301
  end
302
302
 
303
303
  def test_spin_loop_with_interval
@@ -307,7 +307,7 @@ class SpinLoopTest < MiniTest::Test
307
307
  f = spin_loop(interval: 0.01) { buffer << (counter += 1) }
308
308
  sleep 0.02
309
309
  f.stop
310
- assert_in_range 1..3, counter
310
+ assert_in_range 1..3, counter if IS_LINUX
311
311
  end
312
312
 
313
313
  def test_spin_loop_break
@@ -386,10 +386,10 @@ class ThrottledLoopTest < MiniTest::Test
386
386
  counter = 0
387
387
  t0 = Time.now
388
388
  f = spin do
389
- throttled_loop(100) { buffer << (counter += 1) }
389
+ throttled_loop(10) { buffer << (counter += 1) }
390
390
  end
391
- sleep 0.03
392
- assert_in_range 2..4, counter
391
+ sleep 0.3
392
+ assert_in_range 2..4, counter if IS_LINUX
393
393
  end
394
394
 
395
395
  def test_throttled_loop_with_count
@@ -417,8 +417,6 @@ class GlobalAPIEtcTest < MiniTest::Test
417
417
  end
418
418
 
419
419
  def test_every
420
- skip unless IS_LINUX
421
-
422
420
  buffer = []
423
421
  t0 = Time.now
424
422
  f = spin do
@@ -426,12 +424,10 @@ class GlobalAPIEtcTest < MiniTest::Test
426
424
  end
427
425
  sleep 0.05
428
426
  f.stop
429
- assert_in_range 4..6, buffer.size
427
+ assert_in_range 4..6, buffer.size if IS_LINUX
430
428
  end
431
429
 
432
430
  def test_every_with_slow_op
433
- skip unless IS_LINUX
434
-
435
431
  buffer = []
436
432
  t0 = Time.now
437
433
  f = spin do
@@ -439,14 +435,14 @@ class GlobalAPIEtcTest < MiniTest::Test
439
435
  end
440
436
  sleep 0.15
441
437
  f.stop
442
- assert_in_range 2..3, buffer.size
438
+ assert_in_range 2..3, buffer.size if IS_LINUX
443
439
  end
444
440
 
445
441
  def test_sleep
446
442
  t0 = Time.now
447
443
  sleep 0.1
448
444
  elapsed = Time.now - t0
449
- assert (0.05..0.15).include? elapsed if IS_LINUX
445
+ assert_in_range 0.05..0.15, elapsed if IS_LINUX
450
446
 
451
447
  f = spin { sleep }
452
448
  snooze
data/test/test_io.rb CHANGED
@@ -354,9 +354,9 @@ class IOClassMethodsTest < MiniTest::Test
354
354
  skip unless IS_LINUX
355
355
 
356
356
  counter = 0
357
- timer = spin { throttled_loop(200) { counter += 1 } }
357
+ timer = spin { throttled_loop(20) { counter += 1 } }
358
358
 
359
- IO.popen('sleep 0.05') { |io| io.read(8192) }
359
+ IO.popen('sleep 0.5') { |io| io.read(8192) }
360
360
  assert(counter >= 5)
361
361
 
362
362
  result = nil
data/test/test_kernel.rb CHANGED
@@ -51,8 +51,8 @@ class KernelTest < MiniTest::Test
51
51
  counter = 0
52
52
  timer = spin { throttled_loop(200) { counter += 1 } }
53
53
 
54
- `sleep 0.01`
55
- assert(counter >= 2)
54
+ `sleep 0.05`
55
+ assert_in_range 8..14, counter if IS_LINUX
56
56
 
57
57
  result = `echo "hello"`
58
58
  assert_equal "hello\n", result
data/test/test_signal.rb CHANGED
@@ -3,6 +3,63 @@
3
3
  require_relative 'helper'
4
4
 
5
5
  class SignalTrapTest < Minitest::Test
6
+ def test_signal_handler_trace
7
+ i1, o1 = IO.pipe
8
+ i2, o2 = IO.pipe
9
+ pid = Process.pid
10
+ child_pid = Polyphony.fork do
11
+ i1.gets
12
+ Process.kill('SIGINT', pid)
13
+ sleep 0.1
14
+ o2.puts "done"
15
+ o2.close
16
+ end
17
+
18
+ events = []
19
+ begin
20
+ Thread.backend.trace_proc = proc { |*e| events << [e[0], e[1].tag] }
21
+ trap ('SIGINT') { }
22
+
23
+ o1.orig_write("\n")
24
+ o1.close
25
+
26
+ msg = i2.gets
27
+ assert_equal "done\n", msg
28
+ ensure
29
+ Thread.backend.trace_proc = nil
30
+ trap ('SIGINT') { raise Interrupt }
31
+ end
32
+
33
+ Fiber.current.tag = :main
34
+
35
+ expected = [
36
+ [:fiber_switchpoint, :main],
37
+ [:fiber_event_poll_enter, :main],
38
+ [:fiber_create, :oob],
39
+ [:fiber_schedule, :oob],
40
+ [:fiber_event_poll_leave, :main],
41
+ [:fiber_run, :oob],
42
+ [:fiber_terminate, :oob],
43
+ [:fiber_switchpoint, :oob],
44
+ [:fiber_event_poll_enter, :oob],
45
+ [:fiber_schedule, :main],
46
+ [:fiber_event_poll_leave, :oob],
47
+ [:fiber_run, :main]
48
+ ]
49
+ if Thread.backend.kind == :libev
50
+ expected += [
51
+ [:fiber_schedule, :main],
52
+ [:fiber_switchpoint, :main],
53
+ [:fiber_run, :main]
54
+ ]
55
+ end
56
+
57
+ assert_equal expected, events
58
+ ensure
59
+ Process.kill('SIGTERM', child_pid) rescue nil
60
+ Process.wait(child_pid) rescue nil
61
+ end
62
+
6
63
  def test_int_signal
7
64
  Thread.new { sleep 0.001; Process.kill('INT', Process.pid) }
8
65
  assert_raises(Interrupt) { sleep 5 }
data/test/test_socket.rb CHANGED
@@ -9,9 +9,9 @@ class SocketTest < MiniTest::Test
9
9
  super
10
10
  end
11
11
 
12
- def start_tcp_server_on_random_port
12
+ def start_tcp_server_on_random_port(host = '127.0.0.1')
13
13
  port = rand(1100..60000)
14
- server = TCPServer.new('127.0.0.1', port)
14
+ server = TCPServer.new(host, port)
15
15
  [port, server]
16
16
  rescue Errno::EADDRINUSE
17
17
  retry
@@ -40,6 +40,39 @@ class SocketTest < MiniTest::Test
40
40
  server&.close
41
41
  end
42
42
 
43
+ def test_tcpsocket_open_with_hostname
44
+ client = TCPSocket.open('google.com', 80)
45
+ client.write("GET / HTTP/1.0\r\nHost: google.com\r\n\r\n")
46
+ result = nil
47
+ move_on_after(1) {
48
+ result = client.read
49
+ }
50
+ assert result =~ /HTTP\/1.0 301 Moved Permanently/
51
+ end
52
+
53
+ def test_tcp_ipv6
54
+ port, server = start_tcp_server_on_random_port('::1')
55
+ server_fiber = spin do
56
+ while (socket = server.accept)
57
+ spin do
58
+ while (data = socket.gets(8192))
59
+ socket << data
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ snooze
66
+ client = TCPSocket.new('::1', port)
67
+ client.write("1234\n")
68
+ assert_equal "1234\n", client.recv(8192)
69
+ client.close
70
+ ensure
71
+ server_fiber&.stop
72
+ server_fiber&.await
73
+ server&.close
74
+ end
75
+
43
76
  def test_read
44
77
  port, server = start_tcp_server_on_random_port
45
78
  server_fiber = spin do
data/test/test_thread.rb CHANGED
@@ -132,7 +132,7 @@ class ThreadTest < MiniTest::Test
132
132
  Thread.backend.trace_proc = proc {|*r| records << r }
133
133
  suspend
134
134
  assert_equal [
135
- [:fiber_switchpoint, Fiber.current]
135
+ [:fiber_switchpoint, Fiber.current, ["#{__FILE__}:#{__LINE__ - 2}:in `test_that_suspend_returns_immediately_if_no_watchers'"] + caller]
136
136
  ], records
137
137
  ensure
138
138
  Thread.backend.trace_proc = nil
@@ -65,7 +65,7 @@ class ThreadPoolTest < MiniTest::Test
65
65
  end
66
66
  elapsed = Time.now - t0
67
67
 
68
- assert_in_range 0.0..0.009, elapsed
68
+ assert_in_range 0.0..0.009, elapsed if IS_LINUX
69
69
  assert buffer.size < 2
70
70
 
71
71
  sleep 0.15 # allow time for threads to spawn
@@ -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..4, buffer.size
13
+ assert_in_range 1..4, buffer.size if IS_LINUX
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..7, buffer.size
26
+ assert_in_range 4..6, buffer.size if IS_LINUX
27
27
  ensure
28
28
  t.stop
29
29
  end
@@ -34,7 +34,7 @@ class ThrottlerTest < MiniTest::Test
34
34
  f = spin { loop { t.process { buffer << 1 } } }
35
35
  sleep 0.02
36
36
  f.stop
37
- assert_in_range 2..4, buffer.size
37
+ assert_in_range 2..4, buffer.size if IS_LINUX
38
38
  ensure
39
39
  t.stop
40
40
  end
data/test/test_timer.rb CHANGED
@@ -78,7 +78,7 @@ class TimerCancelAfterTest < MiniTest::Test
78
78
 
79
79
  def test_timer_cancel_after_with_reset
80
80
  buf = []
81
- @timer.cancel_after(0.13) do
81
+ @timer.cancel_after(0.15) do
82
82
  sleep 0.05
83
83
  buf << 1
84
84
  @timer.reset
@@ -160,6 +160,6 @@ class TimerMiscTest < MiniTest::Test
160
160
  end
161
161
  sleep 0.05
162
162
  f.stop
163
- assert_in_range 3..7, buffer.size
163
+ assert_in_range 3..7, buffer.size if IS_LINUX
164
164
  end
165
165
  end
data/test/test_trace.rb CHANGED
@@ -10,7 +10,7 @@ class TraceTest < MiniTest::Test
10
10
 
11
11
  assert_equal [
12
12
  [:fiber_schedule, Fiber.current, nil, false],
13
- [:fiber_switchpoint, Fiber.current],
13
+ [:fiber_switchpoint, Fiber.current, ["#{__FILE__}:#{__LINE__ - 4}:in `test_tracing_enabled'"] + caller],
14
14
  [:fiber_run, Fiber.current, nil]
15
15
  ], events
16
16
  ensure
@@ -22,9 +22,15 @@ class TraceTest < MiniTest::Test
22
22
  Thread.backend.trace_proc = proc { |*e| events << e }
23
23
 
24
24
  f = spin { sleep 0; :byebye }
25
+ l0 = __LINE__ + 1
25
26
  suspend
26
27
  sleep 0
27
28
 
29
+ Thread.backend.trace_proc = nil
30
+
31
+ # remove caller info for :fiber_switchpoint events
32
+ events.each {|e| e.pop if e[0] == :fiber_switchpoint }
33
+
28
34
  assert_equal [
29
35
  [:fiber_create, f],
30
36
  [:fiber_schedule, f, nil, false],
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.73.1
4
+ version: '0.77'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-17 00:00:00.000000000 Z
11
+ date: 2022-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -136,7 +136,7 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: 1.1.4
139
- description:
139
+ description:
140
140
  email: sharon@noteflakes.com
141
141
  executables: []
142
142
  extensions:
@@ -146,6 +146,7 @@ extra_rdoc_files:
146
146
  files:
147
147
  - ".github/FUNDING.yml"
148
148
  - ".github/workflows/test.yml"
149
+ - ".github/workflows/test_io_uring.yml"
149
150
  - ".gitignore"
150
151
  - ".gitmodules"
151
152
  - ".rubocop.yml"
@@ -244,6 +245,8 @@ files:
244
245
  - examples/core/thread_pool.rb
245
246
  - examples/core/throttling.rb
246
247
  - examples/core/timeout.rb
248
+ - examples/core/trap1.rb
249
+ - examples/core/trap2.rb
247
250
  - examples/core/using-a-mutex.rb
248
251
  - examples/core/worker-thread.rb
249
252
  - examples/io/backticks.rb
@@ -345,6 +348,7 @@ files:
345
348
  - ext/polyphony/runqueue_ring_buffer.h
346
349
  - ext/polyphony/socket_extensions.c
347
350
  - ext/polyphony/thread.c
351
+ - ext/test_eintr.c
348
352
  - lib/polyphony.rb
349
353
  - lib/polyphony/adapters/fs.rb
350
354
  - lib/polyphony/adapters/irb.rb
@@ -411,7 +415,7 @@ metadata:
411
415
  documentation_uri: https://digital-fabric.github.io/polyphony/
412
416
  homepage_uri: https://digital-fabric.github.io/polyphony/
413
417
  changelog_uri: https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md
414
- post_install_message:
418
+ post_install_message:
415
419
  rdoc_options:
416
420
  - "--title"
417
421
  - polyphony
@@ -430,8 +434,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
430
434
  - !ruby/object:Gem::Version
431
435
  version: '0'
432
436
  requirements: []
433
- rubygems_version: 3.1.6
434
- signing_key:
437
+ rubygems_version: 3.3.3
438
+ signing_key:
435
439
  specification_version: 4
436
440
  summary: Fine grained concurrency for Ruby
437
441
  test_files: []