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 +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +2 -2
- data/examples/adapters/concurrent-ruby.rb +9 -0
- data/examples/core/xx-daemon.rb +14 -0
- data/lib/polyphony.rb +24 -0
- data/lib/polyphony/core/global_api.rb +4 -2
- data/lib/polyphony/core/resource_pool.rb +19 -9
- data/lib/polyphony/extensions/core.rb +6 -0
- data/lib/polyphony/extensions/fiber.rb +0 -12
- data/lib/polyphony/version.rb +1 -1
- data/test/test_agent.rb +2 -2
- data/test/test_fiber.rb +3 -3
- data/test/test_global_api.rb +46 -13
- data/test/test_resource_pool.rb +12 -0
- data/test/test_throttler.rb +6 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f48ef7d009af377b337388a1cca2eb4fe16b85deb1f37076fcd40e010aa9c0e3
|
4
|
+
data.tar.gz: 7b4fa6e8951dc52fd3fdcc9b5a2f1974155e2b709d71de593657374de5020c88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8de0f7526767acef9487417efb79c528312905adc4daed2664e8781ddfffa82118812407c2632269f8c6df0ba91733723804bad027e67cd5094aa1855241d8b4
|
7
|
+
data.tar.gz: 94f5f80ec12f9fba315ce76495f7792125190a2e2004b455a79d58912cac523cd07f90f62218823126023a4003e4f08281676d899737f30d271d5c981f191d04
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.43.
|
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.
|
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,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
|
data/lib/polyphony.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
@@ -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
|
data/lib/polyphony/version.rb
CHANGED
data/test/test_agent.rb
CHANGED
@@ -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
|
-
|
119
|
+
10.times { snooze }
|
120
120
|
|
121
121
|
assert_equal 2, clients.size
|
122
122
|
|
data/test/test_fiber.rb
CHANGED
@@ -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.
|
486
|
-
f2 = spin { sleep 0.
|
487
|
-
f3 = spin { sleep 0.
|
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
|
data/test/test_global_api.rb
CHANGED
@@ -106,7 +106,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
106
106
|
end
|
107
107
|
t1 = Time.now
|
108
108
|
|
109
|
-
assert t1 - t0 < 0.
|
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.
|
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.
|
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
|
-
|
228
|
+
t0 = Time.now
|
229
|
+
f = spin_loop(rate: 10) { buffer << (counter += 1) }
|
205
230
|
sleep 0.2
|
206
231
|
f.stop
|
207
|
-
|
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(
|
244
|
+
throttled_loop(10) { buffer << (counter += 1) }
|
217
245
|
end
|
218
|
-
sleep 0.
|
246
|
+
sleep 0.3
|
219
247
|
f.stop
|
220
|
-
|
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.
|
278
|
+
every(0.1) { buffer << 1 }
|
248
279
|
end
|
249
|
-
sleep 0.
|
280
|
+
sleep 0.5
|
250
281
|
f.stop
|
251
|
-
|
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.
|
289
|
+
sleep 0.1
|
257
290
|
elapsed = Time.now - t0
|
258
|
-
assert (0.
|
291
|
+
assert (0.05..0.15).include? elapsed
|
259
292
|
|
260
293
|
f = spin { sleep }
|
261
294
|
snooze
|
data/test/test_resource_pool.rb
CHANGED
@@ -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
|
data/test/test_throttler.rb
CHANGED
@@ -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(
|
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.
|
11
|
+
sleep 0.2
|
11
12
|
f.stop
|
12
|
-
|
13
|
-
|
14
|
-
assert buffer.size <=
|
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.
|
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-
|
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
|