polyphony 0.38 → 0.43

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +25 -2
  6. data/Gemfile.lock +15 -12
  7. data/README.md +2 -1
  8. data/Rakefile +3 -3
  9. data/TODO.md +27 -97
  10. data/docs/_config.yml +56 -7
  11. data/docs/_sass/custom/custom.scss +0 -30
  12. data/docs/_sass/overrides.scss +0 -46
  13. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  14. data/docs/_user-guide/index.md +9 -0
  15. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  16. data/docs/api-reference/fiber.md +2 -2
  17. data/docs/api-reference/index.md +9 -0
  18. data/docs/api-reference/polyphony-process.md +1 -1
  19. data/docs/api-reference/thread.md +1 -1
  20. data/docs/faq.md +21 -11
  21. data/docs/getting-started/index.md +10 -0
  22. data/docs/getting-started/installing.md +2 -6
  23. data/docs/getting-started/overview.md +486 -0
  24. data/docs/getting-started/tutorial.md +27 -19
  25. data/docs/index.md +1 -1
  26. data/docs/main-concepts/concurrency.md +0 -5
  27. data/docs/main-concepts/design-principles.md +69 -21
  28. data/docs/main-concepts/extending.md +1 -1
  29. data/docs/main-concepts/index.md +9 -0
  30. data/examples/core/01-spinning-up-fibers.rb +1 -0
  31. data/examples/core/03-interrupting.rb +4 -1
  32. data/examples/core/04-handling-signals.rb +19 -0
  33. data/examples/core/xx-agent.rb +102 -0
  34. data/examples/core/xx-sleeping.rb +14 -6
  35. data/examples/io/tunnel.rb +48 -0
  36. data/examples/io/xx-irb.rb +1 -1
  37. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  38. data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
  39. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  40. data/examples/performance/xx-array.rb +11 -0
  41. data/examples/performance/xx-fiber-switch.rb +9 -0
  42. data/examples/performance/xx-snooze.rb +15 -0
  43. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  44. data/ext/{gyro → polyphony}/fiber.c +17 -23
  45. data/ext/{gyro → polyphony}/libev.c +0 -0
  46. data/ext/{gyro → polyphony}/libev.h +0 -0
  47. data/ext/polyphony/libev_agent.c +718 -0
  48. data/ext/polyphony/libev_queue.c +216 -0
  49. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -40
  50. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +19 -39
  51. data/ext/polyphony/polyphony_ext.c +23 -0
  52. data/ext/{gyro → polyphony}/socket.c +21 -18
  53. data/ext/polyphony/thread.c +206 -0
  54. data/ext/{gyro → polyphony}/tracing.c +1 -1
  55. data/lib/polyphony.rb +19 -14
  56. data/lib/polyphony/adapters/irb.rb +1 -1
  57. data/lib/polyphony/adapters/postgres.rb +6 -5
  58. data/lib/polyphony/adapters/process.rb +5 -5
  59. data/lib/polyphony/adapters/trace.rb +28 -28
  60. data/lib/polyphony/core/channel.rb +3 -3
  61. data/lib/polyphony/core/exceptions.rb +1 -1
  62. data/lib/polyphony/core/global_api.rb +13 -11
  63. data/lib/polyphony/core/resource_pool.rb +3 -3
  64. data/lib/polyphony/core/sync.rb +2 -2
  65. data/lib/polyphony/core/thread_pool.rb +6 -6
  66. data/lib/polyphony/core/throttler.rb +13 -6
  67. data/lib/polyphony/event.rb +27 -0
  68. data/lib/polyphony/extensions/core.rb +22 -14
  69. data/lib/polyphony/extensions/fiber.rb +4 -4
  70. data/lib/polyphony/extensions/io.rb +59 -25
  71. data/lib/polyphony/extensions/openssl.rb +36 -16
  72. data/lib/polyphony/extensions/socket.rb +27 -9
  73. data/lib/polyphony/extensions/thread.rb +16 -9
  74. data/lib/polyphony/net.rb +9 -9
  75. data/lib/polyphony/version.rb +1 -1
  76. data/polyphony.gemspec +4 -4
  77. data/test/helper.rb +14 -1
  78. data/test/test_agent.rb +124 -0
  79. data/test/{test_async.rb → test_event.rb} +15 -7
  80. data/test/test_ext.rb +25 -4
  81. data/test/test_fiber.rb +19 -10
  82. data/test/test_global_api.rb +4 -4
  83. data/test/test_io.rb +46 -24
  84. data/test/test_queue.rb +74 -0
  85. data/test/test_signal.rb +3 -40
  86. data/test/test_socket.rb +34 -0
  87. data/test/test_thread.rb +37 -16
  88. data/test/test_trace.rb +6 -5
  89. metadata +40 -43
  90. data/docs/_includes/nav.html +0 -51
  91. data/docs/_includes/prevnext.html +0 -17
  92. data/docs/_layouts/default.html +0 -106
  93. data/docs/api-reference.md +0 -11
  94. data/docs/api-reference/gyro-async.md +0 -57
  95. data/docs/api-reference/gyro-child.md +0 -29
  96. data/docs/api-reference/gyro-queue.md +0 -44
  97. data/docs/api-reference/gyro-timer.md +0 -51
  98. data/docs/api-reference/gyro.md +0 -25
  99. data/docs/getting-started.md +0 -10
  100. data/docs/main-concepts.md +0 -10
  101. data/docs/user-guide.md +0 -10
  102. data/examples/core/forever_sleep.rb +0 -19
  103. data/ext/gyro/async.c +0 -162
  104. data/ext/gyro/child.c +0 -141
  105. data/ext/gyro/gyro_ext.c +0 -33
  106. data/ext/gyro/io.c +0 -489
  107. data/ext/gyro/queue.c +0 -142
  108. data/ext/gyro/selector.c +0 -228
  109. data/ext/gyro/signal.c +0 -133
  110. data/ext/gyro/thread.c +0 -308
  111. data/ext/gyro/timer.c +0 -149
  112. data/test/test_timer.rb +0 -56
@@ -2,26 +2,30 @@
2
2
 
3
3
  require_relative 'helper'
4
4
 
5
- class AsyncTest < MiniTest::Test
6
- def test_that_async_watcher_receives_signal_across_threads
5
+ class EventTest < MiniTest::Test
6
+ def test_that_event_receives_signal_across_threads
7
7
  count = 0
8
- a = Gyro::Async.new
8
+ a = Polyphony::Event.new
9
9
  spin {
10
10
  a.await
11
11
  count += 1
12
12
  }
13
13
  snooze
14
- Thread.new do
14
+ t = Thread.new do
15
15
  orig_sleep 0.001
16
16
  a.signal
17
17
  end
18
18
  suspend
19
19
  assert_equal 1, count
20
+ ensure
21
+ t&.kill
22
+ t&.join
20
23
  end
21
24
 
22
- def test_that_async_watcher_coalesces_signals
25
+ def test_that_event_coalesces_signals
23
26
  count = 0
24
- a = Gyro::Async.new
27
+ a = Polyphony::Event.new
28
+
25
29
  coproc = spin {
26
30
  loop {
27
31
  a.await
@@ -30,11 +34,15 @@ class AsyncTest < MiniTest::Test
30
34
  }
31
35
  }
32
36
  snooze
33
- Thread.new do
37
+ t = Thread.new do
34
38
  orig_sleep 0.001
35
39
  3.times { a.signal }
36
40
  end
41
+
37
42
  coproc.await
38
43
  assert_equal 1, count
44
+ ensure
45
+ t&.kill
46
+ t&.join
39
47
  end
40
48
  end
@@ -74,7 +74,7 @@ class KernelTest < MiniTest::Test
74
74
  $stderr.rewind
75
75
  $stderr = prev_stderr
76
76
 
77
- assert_nil data
77
+ assert_equal '', data
78
78
  assert_equal "error\n", err_io.read
79
79
  ensure
80
80
  $stderr = prev_stderr
@@ -93,6 +93,28 @@ class KernelTest < MiniTest::Test
93
93
  $stdin = prev_stdin
94
94
  end
95
95
 
96
+ def test_multiline_gets
97
+ prev_stdin = $stdin
98
+ i, o = IO.pipe
99
+ $stdin = i
100
+
101
+ spin do
102
+ o << "hello\n"
103
+ o << "world\n"
104
+ o << "nice\n"
105
+ o << "to\n"
106
+ o << "meet\n"
107
+ o << "you\n"
108
+ end
109
+
110
+ s = +''
111
+ 6.times { s << gets }
112
+
113
+ assert_equal "hello\nworld\nnice\nto\nmeet\nyou\n", s
114
+ ensure
115
+ $stdin = prev_stdin
116
+ end
117
+
96
118
  def test_gets_from_argv
97
119
  prev_stdin = $stdin
98
120
 
@@ -103,8 +125,7 @@ class KernelTest < MiniTest::Test
103
125
  count = contents.size
104
126
 
105
127
  buffer = []
106
-
107
- (count * 2).times { buffer << gets }
128
+ (count * 2).times { |i| s = gets; buffer << s }
108
129
  assert_equal contents * 2, buffer
109
130
 
110
131
  i, o = IO.pipe
@@ -172,4 +193,4 @@ class TimeoutTest < MiniTest::Test
172
193
  assert_kind_of MyTimeout, e
173
194
  assert_equal 'foo', e.message
174
195
  end
175
- end
196
+ end
@@ -110,7 +110,7 @@ class FiberTest < MiniTest::Test
110
110
  def test_cross_thread_schedule
111
111
  buffer = []
112
112
  worker_fiber = nil
113
- async = Gyro::Async.new
113
+ async = Polyphony::Event.new
114
114
  worker = Thread.new do
115
115
  worker_fiber = Fiber.current
116
116
  async.signal
@@ -125,10 +125,11 @@ class FiberTest < MiniTest::Test
125
125
  assert_equal [:foo], buffer
126
126
  ensure
127
127
  worker&.kill
128
+ worker&.join
128
129
  end
129
130
 
130
131
  def test_ev_loop_anti_starve_mechanism
131
- async = Gyro::Async.new
132
+ async = Polyphony::Event.new
132
133
  t = Thread.new do
133
134
  f = spin_loop { snooze }
134
135
  sleep 0.001
@@ -139,7 +140,8 @@ class FiberTest < MiniTest::Test
139
140
 
140
141
  assert_equal :foo, result
141
142
  ensure
142
- t.kill if t.alive?
143
+ t&.kill
144
+ t&.join
143
145
  end
144
146
 
145
147
  def test_tag
@@ -350,8 +352,7 @@ class FiberTest < MiniTest::Test
350
352
  result = []
351
353
  f = Fiber.current.spin do
352
354
  result << :start
353
- t = Gyro::Timer.new(1, 0)
354
- result << t.await
355
+ result << Thread.current.agent.sleep(1)
355
356
  end
356
357
  snooze
357
358
  f.interrupt
@@ -624,7 +625,7 @@ class FiberTest < MiniTest::Test
624
625
  end
625
626
  end
626
627
  sleep 0.1
627
- f = spin { Gyro::Child.new(pid).await }
628
+ f = spin { Thread.current.agent.waitpid(pid) }
628
629
  o.close
629
630
  Process.kill('INT', pid)
630
631
  f.await
@@ -646,7 +647,7 @@ class FiberTest < MiniTest::Test
646
647
  end
647
648
  end
648
649
  sleep 0.2
649
- f = spin { Gyro::Child.new(pid).await }
650
+ f = spin { Thread.current.agent.waitpid(pid) }
650
651
  o.close
651
652
  Process.kill('TERM', pid)
652
653
  f.await
@@ -673,7 +674,7 @@ class FiberTest < MiniTest::Test
673
674
  sleep 0.2
674
675
  Process.kill('TERM', pid)
675
676
  end
676
- Gyro::Child.new(pid).await
677
+ Thread.current.agent.waitpid(pid)
677
678
  klass = i.read
678
679
  i.close
679
680
  assert_equal 'Polyphony::Terminate', klass
@@ -707,6 +708,7 @@ class MailboxTest < MiniTest::Test
707
708
  f << i
708
709
  sleep 0
709
710
  end
711
+ sleep 0
710
712
 
711
713
  assert_equal [0, 1, 2], msgs
712
714
  ensure
@@ -717,11 +719,11 @@ class MailboxTest < MiniTest::Test
717
719
  msgs = []
718
720
  f = spin { loop { msgs << receive } }
719
721
 
720
- snooze # allow coproc to start
722
+ snooze # allow f to start
721
723
 
722
724
  3.times { |i| f << i }
723
725
 
724
- sleep 0
726
+ sleep 0.01
725
727
 
726
728
  assert_equal [0, 1, 2], msgs
727
729
  ensure
@@ -743,6 +745,7 @@ class MailboxTest < MiniTest::Test
743
745
  def test_cross_thread_send_receive
744
746
  ping_receive_buffer = []
745
747
  pong_receive_buffer = []
748
+
746
749
  pong = Thread.new do
747
750
  sleep 0.05
748
751
  loop do
@@ -763,9 +766,15 @@ class MailboxTest < MiniTest::Test
763
766
 
764
767
  ping.join
765
768
  pong.kill
769
+ ping = pong = nil
766
770
 
767
771
  assert_equal %w{pong pong pong}, ping_receive_buffer
768
772
  assert_equal %w{ping ping ping}, pong_receive_buffer
773
+ ensure
774
+ pong&.kill
775
+ ping&.kill
776
+ pong&.join
777
+ ping&.join
769
778
  end
770
779
 
771
780
  def test_message_queueing
@@ -202,9 +202,9 @@ class SpinLoopTest < MiniTest::Test
202
202
  buffer = []
203
203
  counter = 0
204
204
  f = spin_loop(rate: 50) { buffer << (counter += 1) }
205
- sleep 0.1
205
+ sleep 0.2
206
206
  f.stop
207
- assert counter >= 5 && counter <= 6
207
+ assert counter >= 9 && counter <= 11
208
208
  end
209
209
  end
210
210
 
@@ -215,9 +215,9 @@ class ThrottledLoopTest < MiniTest::Test
215
215
  f = spin do
216
216
  throttled_loop(50) { buffer << (counter += 1) }
217
217
  end
218
- sleep 0.1
218
+ sleep 0.2
219
219
  f.stop
220
- assert counter >= 5 && counter <= 6
220
+ assert counter >= 9 && counter <= 11
221
221
  end
222
222
 
223
223
  def test_throttled_loop_with_count
@@ -2,30 +2,6 @@
2
2
 
3
3
  require_relative 'helper'
4
4
 
5
- class GyroIOTest < MiniTest::Test
6
- def test_that_reading_works
7
- i, o = IO.pipe
8
- data = +''
9
- sequence = []
10
- watcher = Gyro::IO.new(i, :r)
11
- spin {
12
- sequence << 1
13
- watcher.await
14
- sequence << 2
15
- i.read_nonblock(8192, data)
16
- }
17
- snooze
18
- sequence << 3
19
- spin do
20
- o << 'hello'
21
- sequence << 4
22
- end
23
- suspend
24
- assert_equal 'hello', data
25
- assert_equal [1, 3, 4, 2], sequence
26
- end
27
- end
28
-
29
5
  class IOTest < MiniTest::Test
30
6
  def setup
31
7
  super
@@ -61,6 +37,52 @@ class IOTest < MiniTest::Test
61
37
  @o.close
62
38
  assert_equal 'foobarbaz', @i.read
63
39
  end
40
+
41
+ def test_wait_io
42
+ results = []
43
+ i, o = IO.pipe
44
+ f = spin do
45
+ loop do
46
+ result = i.orig_read_nonblock(8192, exception: false)
47
+ results << result
48
+ case result
49
+ when :wait_readable
50
+ Thread.current.agent.wait_io(i, false)
51
+ else
52
+ break result
53
+ end
54
+ end
55
+ end
56
+
57
+ snooze
58
+ o.write('foo')
59
+ o.close
60
+
61
+ result = f.await
62
+
63
+ assert_equal 'foo', f.await
64
+ assert_equal [:wait_readable, 'foo'], results
65
+ end
66
+
67
+ def test_readpartial
68
+ i, o = IO.pipe
69
+
70
+ o << 'hi'
71
+ assert_equal 'hi', i.readpartial(3)
72
+
73
+ o << 'hi'
74
+ assert_equal 'h', i.readpartial(1)
75
+ assert_equal 'i', i.readpartial(1)
76
+
77
+ spin {
78
+ sleep 0.01
79
+ o << 'hi'
80
+ }
81
+ assert_equal 'hi', i.readpartial(2)
82
+ o.close
83
+
84
+ assert_raises(EOFError) { i.readpartial(1) }
85
+ end
64
86
  end
65
87
 
66
88
  class IOClassMethodsTest < MiniTest::Test
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class QueueTest < MiniTest::Test
6
+ def setup
7
+ super
8
+ @queue = Polyphony::Queue.new
9
+ end
10
+
11
+ def test_pop
12
+ spin {
13
+ @queue << 42
14
+ }
15
+ v = @queue.shift
16
+ assert_equal 42, v
17
+
18
+ (1..4).each { |i| @queue << i }
19
+ buf = []
20
+ 4.times { buf << @queue.shift }
21
+ assert_equal [1, 2, 3, 4], buf
22
+ end
23
+
24
+ def test_multiple_waiters
25
+ a = spin { @queue.shift }
26
+ b = spin { @queue.shift }
27
+
28
+ @queue << :foo
29
+ @queue << :bar
30
+
31
+ assert_equal [:foo, :bar], Fiber.await(a, b)
32
+ end
33
+
34
+ def test_multi_thread_usage
35
+ t = Thread.new { @queue.push :foo }
36
+ assert_equal :foo, @queue.shift
37
+ end
38
+
39
+ def test_shift_each
40
+ (1..4).each { |i| @queue << i }
41
+ buf = []
42
+ @queue.shift_each { |i| buf << i }
43
+ assert_equal [1, 2, 3, 4], buf
44
+ end
45
+
46
+ def test_empty?
47
+ assert @queue.empty?
48
+
49
+ @queue << :foo
50
+ assert !@queue.empty?
51
+
52
+ assert_equal :foo, @queue.shift
53
+ assert @queue.empty?
54
+ end
55
+
56
+ def test_fiber_removal_from_queue
57
+ f1 = spin { @queue.shift }
58
+ f2 = spin { @queue.shift }
59
+ f3 = spin { @queue.shift }
60
+
61
+ # let fibers run
62
+ snooze
63
+
64
+ f2.stop
65
+ snooze
66
+
67
+ @queue << :foo
68
+ @queue << :bar
69
+
70
+ assert_equal :foo, f1.await
71
+ assert_nil f2.await
72
+ assert_equal :bar, f3.await
73
+ end
74
+ end
@@ -2,41 +2,6 @@
2
2
 
3
3
  require_relative 'helper'
4
4
 
5
- class SignalTest < MiniTest::Test
6
- def test_Gyro_Signal_constructor
7
- sig = Signal.list['USR1']
8
- count = 0
9
- w = Gyro::Signal.new(sig)
10
-
11
- spin {
12
- loop {
13
- w.await
14
- count += 1
15
- break
16
- }
17
- }
18
- Thread.new do
19
- orig_sleep 0.001
20
- Process.kill(:USR1, Process.pid)
21
- end
22
- suspend
23
- assert_equal 1, count
24
- end
25
-
26
- def test_wait_for_signal_api
27
- count = 0
28
- spin do
29
- Polyphony.wait_for_signal 'SIGHUP'
30
- count += 1
31
- end
32
-
33
- snooze
34
- Process.kill(:HUP, Process.pid)
35
- snooze
36
- assert_equal 1, count
37
- end
38
- end
39
-
40
5
  class SignalTrapTest < Minitest::Test
41
6
  def test_signal_exception_handling
42
7
  i, o = IO.pipe
@@ -57,9 +22,8 @@ class SignalTrapTest < Minitest::Test
57
22
  end
58
23
  sleep 0.01
59
24
  o.close
60
- watcher = Gyro::Child.new(pid)
61
25
  Process.kill('INT', pid)
62
- watcher.await
26
+ Thread.current.agent.waitpid(pid)
63
27
  buffer = i.read
64
28
  assert_equal "3-interrupt\n", buffer
65
29
  end
@@ -86,9 +50,8 @@ class SignalTrapTest < Minitest::Test
86
50
  end
87
51
  sleep 0.02
88
52
  o.close
89
- watcher = Gyro::Child.new(pid)
90
53
  Process.kill('INT', pid)
91
- watcher.await
54
+ Thread.current.agent.waitpid(pid)
92
55
  buffer = i.read
93
56
  assert_equal "3 - interrupted\n2 - terminated\n1 - terminated\n", buffer
94
57
  end
@@ -130,7 +93,7 @@ class SignalTrapTest < Minitest::Test
130
93
  o.close
131
94
  sleep 0.1
132
95
  Process.kill('INT', pid)
133
- Gyro::Child.new(pid).await
96
+ Thread.current.agent.waitpid(pid)
134
97
  buffer = i.read
135
98
  assert_equal "3-interrupt\n", buffer
136
99
  end