polyphony 0.43.2 → 0.43.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7cf8c345b125c9a41ca7c0ad025ac21d001fc1f73dedb0b4968e426a096d8b6
4
- data.tar.gz: 23b828449792dc3887c75ad54da3451696de66f6aceee255d8a1a5e9489bdf90
3
+ metadata.gz: f48ef7d009af377b337388a1cca2eb4fe16b85deb1f37076fcd40e010aa9c0e3
4
+ data.tar.gz: 7b4fa6e8951dc52fd3fdcc9b5a2f1974155e2b709d71de593657374de5020c88
5
5
  SHA512:
6
- metadata.gz: a35a60f273a1989d31155a0d9c6d0a3685794dc363cb60fbf77cce4f8d4f15f7c16c6489d63d4534dc20b281fe0a9aa65f036ffa478e689d119679db48d7a52f
7
- data.tar.gz: 8de219230c3a1ae3a6c7b618d271a58c54abe8ab5208a338e935c823056b6edebf6d311b4d90748ca9aad238f6b67643a3ac71036a60700920859bcffde56acd
6
+ metadata.gz: 8de0f7526767acef9487417efb79c528312905adc4daed2664e8781ddfffa82118812407c2632269f8c6df0ba91733723804bad027e67cd5094aa1855241d8b4
7
+ data.tar.gz: 94f5f80ec12f9fba315ce76495f7792125190a2e2004b455a79d58912cac523cd07f90f62218823126023a4003e4f08281676d899737f30d271d5c981f191d04
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-latest, macos-latest]
10
+ os: [ubuntu-latest]
11
11
  ruby: [2.6, 2.7]
12
12
 
13
13
  name: >-
@@ -1,3 +1,10 @@
1
+ ## 0.43.3 2020-07-08
2
+
3
+ * Fix behaviour after call to `Process.daemon` (#8)
4
+ * Replace core `Queue` class with `Polyphony::Queue` (#22)
5
+ * Make `ResourcePool` reentrant (#1)
6
+ * Accept `:with_exception` argument in `cancel_after` (#16)
7
+
1
8
  ## 0.43.2 2020-07-07
2
9
 
3
10
  * Fix sending Redis commands with array arguments (#21)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.43.2)
4
+ polyphony (0.43.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -12,7 +12,7 @@ GEM
12
12
  ast (2.4.0)
13
13
  builder (3.2.4)
14
14
  colorator (1.1.0)
15
- concurrent-ruby (1.1.5)
15
+ concurrent-ruby (1.1.6)
16
16
  docile (1.3.2)
17
17
  em-websocket (0.5.1)
18
18
  eventmachine (>= 0.12.9)
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'concurrent'
6
+
7
+ puts "Hello, concurrent-ruby"
8
+
9
+ # this program should not hang with concurrent-ruby 1.1.6 (see issue #22)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ Exception.__disable_sanitized_backtrace__ = true
7
+
8
+ puts "pid: #{Process.pid}"
9
+
10
+ Process.daemon(true, true)
11
+
12
+ Polyphony::ThreadPool.process do
13
+ puts "Hello world from pid #{Process.pid}"
14
+ end
@@ -6,6 +6,12 @@ require_relative './polyphony_ext'
6
6
  module Polyphony
7
7
  # Map Queue to Libev queue implementation
8
8
  Queue = LibevQueue
9
+
10
+ # replace core Queue class with our own
11
+ verbose = $VERBOSE
12
+ $VERBOSE = nil
13
+ Object.const_set(:Queue, Polyphony::Queue)
14
+ $VERBOSE = verbose
9
15
  end
10
16
 
11
17
  require_relative './polyphony/extensions/core'
@@ -114,7 +120,25 @@ module Polyphony
114
120
  threads.each(&:kill)
115
121
  threads.each(&:join)
116
122
  end
123
+
124
+ attr_accessor :original_pid
125
+
126
+ def install_at_exit_handler
127
+ @original_pid = ::Process.pid
128
+
129
+ # This at_exit handler is needed only when the original process exits. Due to
130
+ # the behaviour of fibers on fork (and especially on exit from forked
131
+ # processes,) we use a separate mechanism to terminate fibers in forked
132
+ # processes (see Polyphony.fork).
133
+ at_exit do
134
+ next unless @original_pid == ::Process.pid
135
+
136
+ Polyphony.terminate_threads
137
+ Fiber.current.shutdown_all_children
138
+ end
139
+ end
117
140
  end
118
141
  end
119
142
 
120
143
  Polyphony.install_terminating_signal_handlers
144
+ Polyphony.install_at_exit_handler
@@ -15,11 +15,13 @@ module Polyphony
15
15
  end
16
16
  end
17
17
 
18
- def cancel_after(interval, &block)
18
+ def cancel_after(interval, with_exception: Polyphony::Cancel, &block)
19
19
  fiber = ::Fiber.current
20
20
  canceller = spin do
21
21
  sleep interval
22
- fiber.schedule Polyphony::Cancel.new
22
+ exception = with_exception.is_a?(Class) ?
23
+ with_exception.new : RuntimeError.new(with_exception)
24
+ fiber.schedule exception
23
25
  end
24
26
  block ? cancel_after_wrap_block(canceller, &block) : canceller
25
27
  end
@@ -13,6 +13,7 @@ module Polyphony
13
13
 
14
14
  @stock = []
15
15
  @queue = []
16
+ @acquired_resources = {}
16
17
 
17
18
  @limit = opts[:limit] || 4
18
19
  @size = 0
@@ -23,16 +24,25 @@ module Polyphony
23
24
  end
24
25
 
25
26
  def acquire
26
- Thread.current.agent.ref
27
- resource = wait_for_resource
28
- return unless resource
29
-
30
- yield resource
31
- ensure
32
- Thread.current.agent.unref
33
- release(resource) if resource
27
+ fiber = Fiber.current
28
+ if @acquired_resources[fiber]
29
+ yield @acquired_resources[fiber]
30
+ else
31
+ begin
32
+ Thread.current.agent.ref
33
+ resource = wait_for_resource
34
+ return unless resource
35
+
36
+ @acquired_resources[fiber] = resource
37
+ yield resource
38
+ ensure
39
+ @acquired_resources[fiber] = nil
40
+ Thread.current.agent.unref
41
+ release(resource) if resource
42
+ end
43
+ end
34
44
  end
35
-
45
+
36
46
  def wait_for_resource
37
47
  fiber = Fiber.current
38
48
  @queue << fiber
@@ -57,6 +57,12 @@ module ::Process
57
57
  fiber.define_singleton_method(:pid) { pid }
58
58
  fiber
59
59
  end
60
+
61
+ alias_method :orig_daemon, :daemon
62
+ def daemon(*args)
63
+ orig_daemon(*args)
64
+ Polyphony.original_pid = Process.pid
65
+ end
60
66
  end
61
67
  end
62
68
 
@@ -370,15 +370,3 @@ class ::Fiber
370
370
  end
371
371
 
372
372
  Fiber.current.setup_main_fiber
373
-
374
- # This at_exit handler is needed only when the original process exits. Due to
375
- # the behaviour of fibers on fork (and especially on exit from forked
376
- # processes,) we use a separate mechanism to terminate fibers in forked
377
- # processes (see Polyphony.fork).
378
- orig_pid = Process.pid
379
- at_exit do
380
- next unless orig_pid == Process.pid
381
-
382
- Polyphony.terminate_threads
383
- Fiber.current.shutdown_all_children
384
- end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.43.2'
4
+ VERSION = '0.43.3'
5
5
  end
@@ -111,12 +111,12 @@ class AgentTest < MiniTest::Test
111
111
  end
112
112
 
113
113
  c1 = TCPSocket.new('127.0.0.1', 1234)
114
- snooze
114
+ 10.times { snooze }
115
115
 
116
116
  assert_equal 1, clients.size
117
117
 
118
118
  c2 = TCPSocket.new('127.0.0.1', 1234)
119
- 2.times { snooze }
119
+ 10.times { snooze }
120
120
 
121
121
  assert_equal 2, clients.size
122
122
 
@@ -482,9 +482,9 @@ class FiberTest < MiniTest::Test
482
482
  def test_select_from_multiple_fibers
483
483
  sleep 0
484
484
  buffer = []
485
- f1 = spin { sleep 0.01; buffer << :foo; :foo }
486
- f2 = spin { sleep 0.03; buffer << :bar; :bar }
487
- f3 = spin { sleep 0.05; buffer << :baz; :baz }
485
+ f1 = spin { sleep 0.1; buffer << :foo; :foo }
486
+ f2 = spin { sleep 0.3; buffer << :bar; :bar }
487
+ f3 = spin { sleep 0.5; buffer << :baz; :baz }
488
488
 
489
489
  selected, result = Fiber.select(f1, f2, f3)
490
490
  assert_equal :foo, result
@@ -106,7 +106,7 @@ class MoveOnAfterTest < MiniTest::Test
106
106
  end
107
107
  t1 = Time.now
108
108
 
109
- assert t1 - t0 < 0.03
109
+ assert t1 - t0 < 0.1
110
110
  assert_nil v
111
111
  end
112
112
 
@@ -118,7 +118,7 @@ class MoveOnAfterTest < MiniTest::Test
118
118
  end
119
119
  t1 = Time.now
120
120
 
121
- assert t1 - t0 < 0.02
121
+ assert t1 - t0 < 0.1
122
122
  assert_equal :bar, v
123
123
  end
124
124
 
@@ -129,7 +129,7 @@ class MoveOnAfterTest < MiniTest::Test
129
129
  assert_equal Fiber.current, f.parent
130
130
  v = sleep 1
131
131
  t1 = Time.now
132
- assert t1 - t0 < 0.02
132
+ assert t1 - t0 < 0.1
133
133
  assert_equal 'foo', v
134
134
  end
135
135
  end
@@ -159,6 +159,30 @@ class CancelAfterTest < MiniTest::Test
159
159
  t1 = Time.now
160
160
  assert t1 - t0 < 0.1
161
161
  end
162
+
163
+ class CustomException < Exception
164
+ end
165
+
166
+ def test_cancel_after_with_custom_exception
167
+ assert_raises CustomException do
168
+ cancel_after(0.01, with_exception: CustomException) do
169
+ sleep 1
170
+ :foo
171
+ end
172
+ end
173
+
174
+ begin
175
+ e = nil
176
+ cancel_after(0.01, with_exception: 'foo') do
177
+ sleep 1
178
+ :foo
179
+ end
180
+ rescue => e
181
+ ensure
182
+ assert_kind_of RuntimeError, e
183
+ assert_equal 'foo', e.message
184
+ end
185
+ end
162
186
  end
163
187
 
164
188
 
@@ -201,10 +225,13 @@ class SpinLoopTest < MiniTest::Test
201
225
  def test_spin_loop_with_rate
202
226
  buffer = []
203
227
  counter = 0
204
- f = spin_loop(rate: 50) { buffer << (counter += 1) }
228
+ t0 = Time.now
229
+ f = spin_loop(rate: 10) { buffer << (counter += 1) }
205
230
  sleep 0.2
206
231
  f.stop
207
- assert counter >= 9 && counter <= 11
232
+ elapsed = Time.now - t0
233
+ expected = (elapsed * 10).to_i
234
+ assert counter >= expected - 1 && counter <= expected + 1
208
235
  end
209
236
  end
210
237
 
@@ -212,12 +239,15 @@ class ThrottledLoopTest < MiniTest::Test
212
239
  def test_throttled_loop
213
240
  buffer = []
214
241
  counter = 0
242
+ t0 = Time.now
215
243
  f = spin do
216
- throttled_loop(50) { buffer << (counter += 1) }
244
+ throttled_loop(10) { buffer << (counter += 1) }
217
245
  end
218
- sleep 0.2
246
+ sleep 0.3
219
247
  f.stop
220
- assert counter >= 9 && counter <= 11
248
+ elapsed = Time.now - t0
249
+ expected = (elapsed * 10).to_i
250
+ assert counter >= expected - 1 && counter <= expected + 1
221
251
  end
222
252
 
223
253
  def test_throttled_loop_with_count
@@ -243,19 +273,22 @@ class GlobalAPIEtcTest < MiniTest::Test
243
273
 
244
274
  def test_every
245
275
  buffer = []
276
+ t0 = Time.now
246
277
  f = spin do
247
- every(0.01) { buffer << 1 }
278
+ every(0.1) { buffer << 1 }
248
279
  end
249
- sleep 0.05
280
+ sleep 0.5
250
281
  f.stop
251
- assert (4..5).include?(buffer.size)
282
+ elapsed = Time.now - t0
283
+ expected = (elapsed / 0.1).to_i
284
+ assert buffer.size >= expected - 2 && buffer.size <= expected + 2
252
285
  end
253
286
 
254
287
  def test_sleep
255
288
  t0 = Time.now
256
- sleep 0.05
289
+ sleep 0.1
257
290
  elapsed = Time.now - t0
258
- assert (0.045..0.08).include? elapsed
291
+ assert (0.05..0.15).include? elapsed
259
292
 
260
293
  f = spin { sleep }
261
294
  snooze
@@ -123,4 +123,16 @@ class ResourcePoolTest < MiniTest::Test
123
123
  pool.preheat!
124
124
  assert_equal 2, pool.size
125
125
  end
126
+
127
+ def test_reentrant_resource_pool
128
+ resources = [+'a', +'b']
129
+ pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
130
+
131
+ pool.acquire do |r|
132
+ assert_equal 'a', r
133
+ pool.acquire do |r|
134
+ assert_equal 'a', r
135
+ end
136
+ end
137
+ end
126
138
  end
@@ -4,14 +4,15 @@ require_relative 'helper'
4
4
 
5
5
  class ThrottlerTest < MiniTest::Test
6
6
  def test_throttler_with_rate
7
- t = Polyphony::Throttler.new(100)
7
+ t = Polyphony::Throttler.new(10)
8
8
  buffer = []
9
+ t0 = Time.now
9
10
  f = spin { loop { t.process { buffer << 1 } } }
10
- sleep 0.02
11
+ sleep 0.2
11
12
  f.stop
12
- snooze
13
- assert buffer.size >= 2
14
- assert buffer.size <= 3
13
+ elapsed = Time.now - t0
14
+ expected = (elapsed * 10).to_i
15
+ assert buffer.size >= expected - 1 && buffer.size <= expected + 1
15
16
  ensure
16
17
  t.stop
17
18
  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.43.2
4
+ version: 0.43.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-07 00:00:00.000000000 Z
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -280,6 +280,7 @@ files:
280
280
  - docs/main-concepts/fiber-scheduling.md
281
281
  - docs/main-concepts/index.md
282
282
  - docs/polyphony-logo.png
283
+ - examples/adapters/concurrent-ruby.rb
283
284
  - examples/adapters/pg_client.rb
284
285
  - examples/adapters/pg_notify.rb
285
286
  - examples/adapters/pg_pool.rb
@@ -297,6 +298,7 @@ files:
297
298
  - examples/core/xx-at_exit.rb
298
299
  - examples/core/xx-caller.rb
299
300
  - examples/core/xx-channels.rb
301
+ - examples/core/xx-daemon.rb
300
302
  - examples/core/xx-deadlock.rb
301
303
  - examples/core/xx-deferring-an-operation.rb
302
304
  - examples/core/xx-erlang-style-genserver.rb