polyphony 0.49.1 → 0.52.0

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +35 -0
  4. data/Gemfile.lock +7 -68
  5. data/TODO.md +6 -0
  6. data/examples/core/forking.rb +2 -2
  7. data/examples/core/nested.rb +21 -0
  8. data/examples/core/suspend.rb +13 -0
  9. data/examples/core/terminate_main_fiber.rb +12 -0
  10. data/examples/performance/thread-vs-fiber/polyphony_server.rb +2 -4
  11. data/ext/polyphony/backend_common.h +58 -8
  12. data/ext/polyphony/backend_io_uring.c +158 -35
  13. data/ext/polyphony/backend_libev.c +192 -25
  14. data/ext/polyphony/event.c +1 -1
  15. data/ext/polyphony/extconf.rb +7 -2
  16. data/ext/polyphony/fiber.c +2 -1
  17. data/ext/polyphony/polyphony.c +94 -0
  18. data/ext/polyphony/polyphony.h +29 -2
  19. data/ext/polyphony/queue.c +1 -1
  20. data/ext/polyphony/runqueue.c +7 -1
  21. data/ext/polyphony/runqueue_ring_buffer.c +9 -0
  22. data/ext/polyphony/runqueue_ring_buffer.h +1 -0
  23. data/ext/polyphony/thread.c +14 -0
  24. data/lib/polyphony/adapters/irb.rb +1 -1
  25. data/lib/polyphony/adapters/mysql2.rb +1 -1
  26. data/lib/polyphony/adapters/postgres.rb +5 -5
  27. data/lib/polyphony/adapters/process.rb +4 -4
  28. data/lib/polyphony/core/exceptions.rb +1 -0
  29. data/lib/polyphony/core/global_api.rb +6 -6
  30. data/lib/polyphony/core/sync.rb +1 -1
  31. data/lib/polyphony/core/throttler.rb +1 -1
  32. data/lib/polyphony/core/timer.rb +63 -20
  33. data/lib/polyphony/extensions/core.rb +5 -5
  34. data/lib/polyphony/extensions/fiber.rb +11 -8
  35. data/lib/polyphony/extensions/io.rb +13 -22
  36. data/lib/polyphony/extensions/openssl.rb +6 -6
  37. data/lib/polyphony/extensions/socket.rb +41 -41
  38. data/lib/polyphony/extensions/thread.rb +1 -2
  39. data/lib/polyphony/version.rb +1 -1
  40. data/polyphony.gemspec +6 -5
  41. data/test/helper.rb +2 -3
  42. data/test/stress.rb +2 -0
  43. data/test/test_backend.rb +58 -5
  44. data/test/test_fiber.rb +31 -0
  45. data/test/test_global_api.rb +2 -2
  46. data/test/test_io.rb +84 -1
  47. data/test/test_kernel.rb +1 -1
  48. data/test/test_signal.rb +2 -3
  49. data/test/test_socket.rb +61 -0
  50. data/test/test_timer.rb +41 -8
  51. metadata +21 -60
data/test/test_fiber.rb CHANGED
@@ -349,6 +349,22 @@ class FiberTest < MiniTest::Test
349
349
  assert_equal [:foo, :terminate], buffer
350
350
  end
351
351
 
352
+ CMD_TERMINATE_MAIN_FIBER = <<~BASH
353
+ ruby -rbundler/setup -rpolyphony -e"spin { sleep 0.1; Thread.current.main_fiber.terminate }; begin; sleep; rescue Polyphony::Terminate; STDOUT << 'terminated'; end" 2>&1
354
+ BASH
355
+
356
+ CMD_TERMINATE_CHILD_FIBER = <<~BASH
357
+ ruby -rbundler/setup -rpolyphony -e"f = spin { sleep }; spin { sleep 0.1; f.terminate }; f.await" 2>&1
358
+ BASH
359
+
360
+ def test_terminate_main_fiber
361
+ output = `#{CMD_TERMINATE_CHILD_FIBER}`
362
+ assert_equal '', output
363
+
364
+ output = `#{CMD_TERMINATE_MAIN_FIBER}`
365
+ assert_equal 'terminated', output
366
+ end
367
+
352
368
  def test_interrupt_timer
353
369
  result = []
354
370
  f = Fiber.current.spin do
@@ -1038,6 +1054,21 @@ class RestartTest < MiniTest::Test
1038
1054
  end
1039
1055
  end
1040
1056
 
1057
+ class ChildrenTerminationTest < MiniTest::Test
1058
+ def test_shutdown_all_children
1059
+ f = spin do
1060
+ 1000.times { spin { suspend } }
1061
+ suspend
1062
+ end
1063
+
1064
+ snooze
1065
+ assert_equal 1000, f.children.size
1066
+
1067
+ f.shutdown_all_children
1068
+ assert_equal 0, f.children.size
1069
+ end
1070
+ end
1071
+
1041
1072
  class GracefulTerminationTest < MiniTest::Test
1042
1073
  def test_graceful_termination
1043
1074
  buffer = []
@@ -160,10 +160,10 @@ class MoveOnAfterTest < MiniTest::Test
160
160
  end
161
161
  t1 = Time.now
162
162
  assert_equal 1, o
163
- assert_in_range 0.008..0.013, t1 - t0
163
+ assert_in_range 0.008..0.015, t1 - t0
164
164
 
165
165
  t0 = Time.now
166
- o = move_on_after(0.02, with_value: 1) do
166
+ o = move_on_after(0.05, with_value: 1) do
167
167
  move_on_after(0.01, with_value: 2) do
168
168
  sleep 1
169
169
  end
data/test/test_io.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'helper'
4
+ require 'msgpack'
4
5
 
5
6
  class IOTest < MiniTest::Test
6
7
  def setup
@@ -110,7 +111,7 @@ class IOTest < MiniTest::Test
110
111
  assert_equal [], buf
111
112
 
112
113
  o << "ulous\n"
113
- 10.times { snooze }
114
+ sleep 0.01
114
115
  assert_equal ["fabulous\n"], buf
115
116
 
116
117
  o.close
@@ -173,6 +174,88 @@ class IOTest < MiniTest::Test
173
174
 
174
175
  assert_equal 'hello: world', buf
175
176
  end
177
+
178
+ def test_feed_loop_with_block
179
+ i, o = IO.pipe
180
+ unpacker = MessagePack::Unpacker.new
181
+ buffer = []
182
+ reader = spin do
183
+ i.feed_loop(unpacker, :feed_each) { |msg| buffer << msg }
184
+ end
185
+ o << 'foo'.to_msgpack
186
+ sleep 0.01
187
+ assert_equal ['foo'], buffer
188
+
189
+ o << 'bar'.to_msgpack
190
+ sleep 0.01
191
+ assert_equal ['foo', 'bar'], buffer
192
+
193
+ o << 'baz'.to_msgpack
194
+ sleep 0.01
195
+ assert_equal ['foo', 'bar', 'baz'], buffer
196
+ end
197
+
198
+ class Receiver1
199
+ attr_reader :buffer
200
+
201
+ def initialize
202
+ @buffer = []
203
+ end
204
+
205
+ def recv(obj)
206
+ @buffer << obj
207
+ end
208
+ end
209
+
210
+ def test_feed_loop_without_block
211
+ i, o = IO.pipe
212
+ receiver = Receiver1.new
213
+ reader = spin do
214
+ i.feed_loop(receiver, :recv)
215
+ end
216
+ o << 'foo'
217
+ sleep 0.01
218
+ assert_equal ['foo'], receiver.buffer
219
+
220
+ o << 'bar'
221
+ sleep 0.01
222
+ assert_equal ['foo', 'bar'], receiver.buffer
223
+
224
+ o << 'baz'
225
+ sleep 0.01
226
+ assert_equal ['foo', 'bar', 'baz'], receiver.buffer
227
+ end
228
+
229
+ class Receiver2
230
+ attr_reader :buffer
231
+
232
+ def initialize
233
+ @buffer = []
234
+ end
235
+
236
+ def call(obj)
237
+ @buffer << obj
238
+ end
239
+ end
240
+
241
+ def test_feed_loop_without_method
242
+ i, o = IO.pipe
243
+ receiver = Receiver2.new
244
+ reader = spin do
245
+ i.feed_loop(receiver)
246
+ end
247
+ o << 'foo'
248
+ sleep 0.01
249
+ assert_equal ['foo'], receiver.buffer
250
+
251
+ o << 'bar'
252
+ sleep 0.01
253
+ assert_equal ['foo', 'bar'], receiver.buffer
254
+
255
+ o << 'baz'
256
+ sleep 0.01
257
+ assert_equal ['foo', 'bar', 'baz'], receiver.buffer
258
+ end
176
259
  end
177
260
 
178
261
  class IOClassMethodsTest < MiniTest::Test
data/test/test_kernel.rb CHANGED
@@ -21,7 +21,7 @@ class KernelTest < MiniTest::Test
21
21
 
22
22
  def test_Kernel_system_singleton_method
23
23
  assert_equal true, Kernel.system("which ruby > /dev/null 2>&1")
24
- assert_equal false, Kernel.system("which rruubbyy > /dev/null 2>&1")
24
+ assert_equal false, Kernel.system("azertyuiop > /dev/null 2>&1")
25
25
  end
26
26
 
27
27
  def patch_open3
data/test/test_signal.rb CHANGED
@@ -23,7 +23,7 @@ class SignalTrapTest < Minitest::Test
23
23
  ensure
24
24
  o.close
25
25
  end
26
- sleep 0.01
26
+ sleep 0.1
27
27
  o.close
28
28
  Process.kill('INT', pid)
29
29
  Thread.current.backend.waitpid(pid)
@@ -46,8 +46,7 @@ class SignalTrapTest < Minitest::Test
46
46
  end.await
47
47
  rescue Interrupt
48
48
  o.puts "3 - interrupted"
49
- Fiber.current.terminate_all_children
50
- Fiber.current.await_all_children
49
+ Fiber.current.shutdown_all_children
51
50
  ensure
52
51
  o.close
53
52
  end
data/test/test_socket.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'helper'
4
4
  require 'fileutils'
5
+ require 'msgpack'
5
6
 
6
7
  class SocketTest < MiniTest::Test
7
8
  def setup
@@ -33,6 +34,66 @@ class SocketTest < MiniTest::Test
33
34
  server&.close
34
35
  end
35
36
 
37
+ # sending multiple strings at once
38
+ def test_sendv
39
+ port = rand(1234..5678)
40
+ server = TCPServer.new('127.0.0.1', port)
41
+
42
+ server_fiber = spin do
43
+ while (socket = server.accept)
44
+ spin do
45
+ while (data = socket.gets(8192))
46
+ socket.write("you said ", data)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ snooze
53
+ client = TCPSocket.new('127.0.0.1', port)
54
+ client.write("1234\n")
55
+ assert_equal "you said 1234\n", client.recv(8192)
56
+ client.close
57
+ ensure
58
+ server_fiber&.stop
59
+ server_fiber&.await
60
+ server&.close
61
+ end
62
+
63
+
64
+ def test_feed_loop
65
+ port = rand(1234..5678)
66
+ server = TCPServer.new('127.0.0.1', port)
67
+
68
+ server_fiber = spin do
69
+ reader = MessagePack::Unpacker.new
70
+ while (socket = server.accept)
71
+ spin do
72
+ socket.feed_loop(reader, :feed_each) do |msg|
73
+ msg = { 'result' => msg['x'] + msg['y'] }
74
+ socket << msg.to_msgpack
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ snooze
81
+ client = TCPSocket.new('127.0.0.1', port)
82
+ reader = MessagePack::Unpacker.new
83
+ client << { 'x' => 13, 'y' => 14 }.to_msgpack
84
+ result = nil
85
+ client.feed_loop(reader, :feed_each) do |msg|
86
+ result = msg
87
+ break
88
+ end
89
+ assert_equal({ 'result' => 27}, result)
90
+ client.close
91
+ ensure
92
+ server_fiber&.stop
93
+ server_fiber&.await
94
+ server&.close
95
+ end
96
+
36
97
  def test_unix_socket
37
98
  path = '/tmp/test_unix_socket'
38
99
  FileUtils.rm(path) rescue nil
data/test/test_timer.rb CHANGED
@@ -11,7 +11,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
11
11
  @timer.stop
12
12
  end
13
13
 
14
- def test_move_on_after
14
+ def test_timer_move_on_after
15
15
  t0 = Time.now
16
16
  v = @timer.move_on_after(0.1) do
17
17
  sleep 1
@@ -23,7 +23,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
23
23
  assert_nil v
24
24
  end
25
25
 
26
- def test_move_on_after_with_value
26
+ def test_timer_move_on_after_with_value
27
27
  t0 = Time.now
28
28
  v = @timer.move_on_after(0.01, with_value: :bar) do
29
29
  sleep 1
@@ -35,7 +35,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
35
35
  assert_equal :bar, v
36
36
  end
37
37
 
38
- def test_move_on_after_with_reset
38
+ def test_timer_move_on_after_with_reset
39
39
  t0 = Time.now
40
40
  v = @timer.move_on_after(0.01, with_value: :moved_on) do
41
41
  sleep 0.007
@@ -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.02..0.03, t1 - t0
51
+ assert_in_range 0.015..0.03, t1 - t0
52
52
  end
53
53
  end
54
54
 
@@ -61,7 +61,7 @@ class TimerCancelAfterTest < MiniTest::Test
61
61
  @timer.stop
62
62
  end
63
63
 
64
- def test_cancel_after
64
+ def test_timer_cancel_after
65
65
  t0 = Time.now
66
66
 
67
67
  assert_raises Polyphony::Cancel do
@@ -74,7 +74,7 @@ class TimerCancelAfterTest < MiniTest::Test
74
74
  assert_in_range 0.01..0.03, t1 - t0
75
75
  end
76
76
 
77
- def test_cancel_after_with_reset
77
+ def test_timer_cancel_after_with_reset
78
78
  t0 = Time.now
79
79
  @timer.cancel_after(0.01) do
80
80
  sleep 0.007
@@ -82,13 +82,13 @@ class TimerCancelAfterTest < MiniTest::Test
82
82
  sleep 0.007
83
83
  end
84
84
  t1 = Time.now
85
- assert_in_range 0.014..0.024, t1 - t0
85
+ assert_in_range 0.012..0.024, t1 - t0
86
86
  end
87
87
 
88
88
  class CustomException < Exception
89
89
  end
90
90
 
91
- def test_cancel_after_with_custom_exception
91
+ def test_timer_cancel_after_with_custom_exception
92
92
  assert_raises CustomException do
93
93
  @timer.cancel_after(0.01, with_exception: CustomException) do
94
94
  sleep 1
@@ -122,3 +122,36 @@ class TimerCancelAfterTest < MiniTest::Test
122
122
  end
123
123
  end
124
124
  end
125
+
126
+ class TimerMiscTest < MiniTest::Test
127
+ def setup
128
+ @timer = Polyphony::Timer.new(resolution: 0.001)
129
+ sleep 0
130
+ end
131
+
132
+ def teardown
133
+ @timer.stop
134
+ end
135
+
136
+ def test_timer_after
137
+ buffer = []
138
+ f = @timer.after(0.01) { buffer << 2 }
139
+ assert_kind_of Fiber, f
140
+ snooze
141
+ assert_equal [], buffer
142
+ sleep 0.1
143
+ p :post_sleep
144
+ assert_equal [2], buffer
145
+ end
146
+
147
+ def test_timer_every
148
+ buffer = []
149
+ t0 = Time.now
150
+ f = spin do
151
+ @timer.every(0.01) { buffer << 1 }
152
+ end
153
+ sleep 0.05
154
+ f.stop
155
+ assert_in_range 4..6, buffer.size
156
+ end
157
+ end
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.49.1
4
+ version: 0.52.0
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-01-13 00:00:00.000000000 Z
11
+ date: 2021-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 5.13.0
33
+ version: 5.14.4
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 5.13.0
40
+ version: 5.14.4
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest-reporters
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
96
  version: 0.13.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: msgpack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.4.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.4.2
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: pg
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -212,62 +226,6 @@ dependencies:
212
226
  - - '='
213
227
  - !ruby/object:Gem::Version
214
228
  version: 0.17.1
215
- - !ruby/object:Gem::Dependency
216
- name: jekyll
217
- requirement: !ruby/object:Gem::Requirement
218
- requirements:
219
- - - "~>"
220
- - !ruby/object:Gem::Version
221
- version: 3.8.6
222
- type: :development
223
- prerelease: false
224
- version_requirements: !ruby/object:Gem::Requirement
225
- requirements:
226
- - - "~>"
227
- - !ruby/object:Gem::Version
228
- version: 3.8.6
229
- - !ruby/object:Gem::Dependency
230
- name: jekyll-remote-theme
231
- requirement: !ruby/object:Gem::Requirement
232
- requirements:
233
- - - "~>"
234
- - !ruby/object:Gem::Version
235
- version: 0.4.1
236
- type: :development
237
- prerelease: false
238
- version_requirements: !ruby/object:Gem::Requirement
239
- requirements:
240
- - - "~>"
241
- - !ruby/object:Gem::Version
242
- version: 0.4.1
243
- - !ruby/object:Gem::Dependency
244
- name: jekyll-seo-tag
245
- requirement: !ruby/object:Gem::Requirement
246
- requirements:
247
- - - "~>"
248
- - !ruby/object:Gem::Version
249
- version: 2.6.1
250
- type: :development
251
- prerelease: false
252
- version_requirements: !ruby/object:Gem::Requirement
253
- requirements:
254
- - - "~>"
255
- - !ruby/object:Gem::Version
256
- version: 2.6.1
257
- - !ruby/object:Gem::Dependency
258
- name: just-the-docs
259
- requirement: !ruby/object:Gem::Requirement
260
- requirements:
261
- - - "~>"
262
- - !ruby/object:Gem::Version
263
- version: 0.3.0
264
- type: :development
265
- prerelease: false
266
- version_requirements: !ruby/object:Gem::Requirement
267
- requirements:
268
- - - "~>"
269
- - !ruby/object:Gem::Version
270
- version: 0.3.0
271
229
  description:
272
230
  email: sharon@noteflakes.com
273
231
  executables: []
@@ -353,6 +311,7 @@ files:
353
311
  - examples/core/forking.rb
354
312
  - examples/core/handling-signals.rb
355
313
  - examples/core/interrupt.rb
314
+ - examples/core/nested.rb
356
315
  - examples/core/pingpong.rb
357
316
  - examples/core/recurrent-timer.rb
358
317
  - examples/core/resource_delegate.rb
@@ -360,6 +319,8 @@ files:
360
319
  - examples/core/spin_error_backtrace.rb
361
320
  - examples/core/supervise-process.rb
362
321
  - examples/core/supervisor.rb
322
+ - examples/core/suspend.rb
323
+ - examples/core/terminate_main_fiber.rb
363
324
  - examples/core/thread-sleep.rb
364
325
  - examples/core/thread_pool.rb
365
326
  - examples/core/throttling.rb