polyphony 0.26 → 0.27
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/.rubocop.yml +3 -3
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +34 -14
- data/examples/core/xx-mt-scheduler.rb +349 -0
- data/examples/core/xx-queue-async.rb +120 -0
- data/examples/core/xx-readpartial.rb +18 -0
- data/examples/core/xx-thread-selector-sleep.rb +51 -0
- data/examples/core/xx-thread-selector-snooze.rb +46 -0
- data/examples/core/xx-thread-sleep.rb +17 -0
- data/examples/core/xx-thread-snooze.rb +34 -0
- data/examples/performance/snooze.rb +5 -5
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +73 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +12 -4
- data/ext/gyro/async.c +58 -61
- data/ext/gyro/child.c +50 -59
- data/ext/gyro/gyro.c +84 -192
- data/ext/gyro/gyro.h +29 -2
- data/ext/gyro/gyro_ext.c +6 -0
- data/ext/gyro/io.c +87 -96
- data/ext/gyro/queue.c +109 -0
- data/ext/gyro/selector.c +117 -0
- data/ext/gyro/signal.c +44 -54
- data/ext/gyro/socket.c +15 -20
- data/ext/gyro/thread.c +203 -0
- data/ext/gyro/timer.c +79 -50
- data/ext/libev/ev.c +3 -0
- data/lib/polyphony.rb +7 -12
- data/lib/polyphony/core/global_api.rb +5 -1
- data/lib/polyphony/core/throttler.rb +6 -11
- data/lib/polyphony/extensions/core.rb +2 -0
- data/lib/polyphony/extensions/fiber.rb +11 -13
- data/lib/polyphony/extensions/thread.rb +52 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +8 -3
- data/test/test_fiber.rb +2 -2
- data/test/test_global_api.rb +4 -5
- data/test/test_gyro.rb +3 -2
- data/test/test_io.rb +1 -0
- data/test/test_supervisor.rb +3 -3
- data/test/test_thread.rb +44 -0
- data/test/test_throttler.rb +41 -0
- data/test/test_timer.rb +13 -7
- metadata +17 -6
- data/examples/core/xx-thread_cancel.rb +0 -28
- data/examples/performance/thread.rb +0 -27
- data/lib/polyphony/core/thread.rb +0 -23
data/test/test_fiber.rb
CHANGED
@@ -327,8 +327,8 @@ class FiberTest < MiniTest::Test
|
|
327
327
|
def test_select_from_multiple_fibers
|
328
328
|
buffer = []
|
329
329
|
f1 = spin { sleep 0.01; buffer << :foo; :foo }
|
330
|
-
f2 = spin { sleep 0.
|
331
|
-
f3 = spin { sleep 0.
|
330
|
+
f2 = spin { sleep 0.03; buffer << :bar; :bar }
|
331
|
+
f3 = spin { sleep 0.05; buffer << :baz; :baz }
|
332
332
|
|
333
333
|
result, selected = Fiber.select(f1, f2, f3)
|
334
334
|
assert_equal :foo, result
|
data/test/test_global_api.rb
CHANGED
@@ -63,7 +63,7 @@ class CancelScopeTest < Minitest::Test
|
|
63
63
|
# async operation will only begin on next iteration of event loop
|
64
64
|
assert_nil ctx[:cancel_scope]
|
65
65
|
|
66
|
-
|
66
|
+
Thread.current.switch_fiber
|
67
67
|
assert_kind_of Polyphony::CancelScope, ctx[:cancel_scope]
|
68
68
|
assert_kind_of Polyphony::Cancel, ctx[:result]
|
69
69
|
end
|
@@ -75,7 +75,7 @@ class CancelScopeTest < Minitest::Test
|
|
75
75
|
sleep_with_cancel(ctx, :stop)
|
76
76
|
end
|
77
77
|
|
78
|
-
|
78
|
+
Thread.current.switch_fiber
|
79
79
|
assert ctx[:cancel_scope]
|
80
80
|
assert_nil ctx[:result]
|
81
81
|
end
|
@@ -272,7 +272,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
272
272
|
end
|
273
273
|
sleep 0.1
|
274
274
|
f.stop
|
275
|
-
|
275
|
+
assert counter >= 5 && counter <= 6
|
276
276
|
end
|
277
277
|
|
278
278
|
def test_throttled_loop_with_count
|
@@ -299,8 +299,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
299
299
|
t0 = Time.now
|
300
300
|
sleep 0.05
|
301
301
|
elapsed = Time.now - t0
|
302
|
-
|
303
|
-
assert (0.045..0.8).include? elapsed
|
302
|
+
assert (0.045..0.08).include? elapsed
|
304
303
|
|
305
304
|
f = spin { sleep }
|
306
305
|
snooze
|
data/test/test_gyro.rb
CHANGED
@@ -109,6 +109,7 @@ class GyroTest < MiniTest::Test
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def test_break
|
112
|
+
skip "break is still not implemented for new scheduler"
|
112
113
|
values = []
|
113
114
|
Fiber.new do
|
114
115
|
values << :foo
|
@@ -137,7 +138,7 @@ class GyroTest < MiniTest::Test
|
|
137
138
|
end.schedule
|
138
139
|
|
139
140
|
f2 = Fiber.new do
|
140
|
-
|
141
|
+
Thread.current.reset_fiber_scheduling
|
141
142
|
values << :restarted
|
142
143
|
snooze
|
143
144
|
values << :baz
|
@@ -161,7 +162,7 @@ class GyroTest < MiniTest::Test
|
|
161
162
|
end.schedule
|
162
163
|
|
163
164
|
Fiber.new do
|
164
|
-
|
165
|
+
Thread.current.reset_fiber_scheduling
|
165
166
|
|
166
167
|
# control is transfer to the fiber that called Gyro.restart
|
167
168
|
values << :restarted
|
data/test/test_io.rb
CHANGED
data/test/test_supervisor.rb
CHANGED
@@ -55,9 +55,9 @@ class SupervisorTest < MiniTest::Test
|
|
55
55
|
buffer = []
|
56
56
|
foo_f = bar_f = baz_f = nil
|
57
57
|
result, f = Polyphony::Supervisor.new.select { |s|
|
58
|
-
foo_f = s.spin { sleep 0.
|
59
|
-
bar_f = s.spin { sleep 0.
|
60
|
-
baz_f = s.spin { sleep 0.
|
58
|
+
foo_f = s.spin { sleep 0.1; buffer << :foo; :foo }
|
59
|
+
bar_f = s.spin { sleep 0.3; buffer << :bar; :bar }
|
60
|
+
baz_f = s.spin { sleep 0.5; buffer << :baz; :baz }
|
61
61
|
}
|
62
62
|
|
63
63
|
assert_equal :foo, result
|
data/test/test_thread.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class ThreadTest < MiniTest::Test
|
6
|
+
def test_thread_spin
|
7
|
+
buffer = []
|
8
|
+
spin { (1..3).each { |i| snooze; buffer << i } }
|
9
|
+
t = Thread.new do
|
10
|
+
s1 = spin { (11..13).each { |i| snooze; buffer << i } }
|
11
|
+
s2 = spin { (21..23).each { |i| snooze; buffer << i } }
|
12
|
+
Fiber.join(s1, s2)
|
13
|
+
end
|
14
|
+
t.join
|
15
|
+
|
16
|
+
assert_equal [1, 2, 3, 11, 12, 13, 21, 22, 23], buffer.sort
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_thread_join
|
20
|
+
buffer = []
|
21
|
+
spin { (1..3).each { |i| snooze; buffer << i } }
|
22
|
+
t = Thread.new { sleep 0.01; buffer << 4 }
|
23
|
+
r = t.join
|
24
|
+
|
25
|
+
assert_equal [1, 2, 3, 4], buffer
|
26
|
+
assert_equal t, r
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_thread_join_with_timeout
|
30
|
+
buffer = []
|
31
|
+
spin { (1..3).each { |i| snooze; buffer << i } }
|
32
|
+
t = Thread.new { sleep 1; buffer << 4 }
|
33
|
+
t0 = Time.now
|
34
|
+
r = t.join(0.01)
|
35
|
+
|
36
|
+
assert Time.now - t0 < 0.2
|
37
|
+
assert_equal [1, 2, 3], buffer
|
38
|
+
assert_nil r
|
39
|
+
ensure
|
40
|
+
# killing the thread will prevent stopping the sleep timer, as well as the
|
41
|
+
# thread's event selector, leading to a memory leak.
|
42
|
+
t.kill if t.alive?
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class ThrottlerTest < MiniTest::Test
|
6
|
+
def test_throttler_with_rate
|
7
|
+
t = Polyphony::Throttler.new(100)
|
8
|
+
buffer = []
|
9
|
+
f = spin { loop { t.process { buffer << 1 } } }
|
10
|
+
sleep 0.02
|
11
|
+
f.stop
|
12
|
+
assert buffer.size >= 2
|
13
|
+
assert buffer.size <= 3
|
14
|
+
ensure
|
15
|
+
t.stop
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_throttler_with_hash_of_rate
|
19
|
+
t = Polyphony::Throttler.new(rate: 100)
|
20
|
+
buffer = []
|
21
|
+
f = spin { loop { t.process { buffer << 1 } } }
|
22
|
+
sleep 0.02
|
23
|
+
f.stop
|
24
|
+
assert buffer.size >= 2
|
25
|
+
assert buffer.size <= 3
|
26
|
+
ensure
|
27
|
+
t.stop
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_throttler_with_hash_of_interval
|
31
|
+
t = Polyphony::Throttler.new(interval: 0.01)
|
32
|
+
buffer = []
|
33
|
+
f = spin { loop { t.process { buffer << 1 } } }
|
34
|
+
sleep 0.02
|
35
|
+
f.stop
|
36
|
+
assert buffer.size >= 2
|
37
|
+
assert buffer.size <= 3
|
38
|
+
ensure
|
39
|
+
t.stop
|
40
|
+
end
|
41
|
+
end
|
data/test/test_timer.rb
CHANGED
@@ -26,25 +26,31 @@ class TimerTest < MiniTest::Test
|
|
26
26
|
}
|
27
27
|
suspend
|
28
28
|
assert_equal 3, count
|
29
|
+
ensure
|
30
|
+
t.stop
|
29
31
|
end
|
30
32
|
|
31
33
|
def test_that_repeating_timer_compensates_for_drift
|
32
34
|
count = 0
|
33
|
-
t = Gyro::Timer.new(0.
|
34
|
-
|
35
|
+
t = Gyro::Timer.new(0.1, 0.1)
|
36
|
+
deltas = []
|
35
37
|
last = nil
|
36
38
|
spin {
|
37
39
|
last = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
38
40
|
loop {
|
39
41
|
t.await
|
40
|
-
|
42
|
+
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
43
|
+
elapsed = (now - last)
|
44
|
+
deltas << elapsed
|
45
|
+
last = now
|
41
46
|
count += 1
|
42
|
-
sleep 0.
|
43
|
-
break if count >=
|
47
|
+
sleep 0.05
|
48
|
+
break if count >= 3
|
44
49
|
}
|
45
50
|
}
|
46
51
|
suspend
|
47
|
-
|
48
|
-
|
52
|
+
assert_equal 0, deltas[1..-1].filter { |d| (d - 0.1).abs >= 0.05 }.size
|
53
|
+
ensure
|
54
|
+
t.stop
|
49
55
|
end
|
50
56
|
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.
|
4
|
+
version: '0.27'
|
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-01-
|
11
|
+
date: 2020-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: modulation
|
@@ -203,6 +203,9 @@ files:
|
|
203
203
|
- examples/core/xx-extended_fibers.rb
|
204
204
|
- examples/core/xx-forking.rb
|
205
205
|
- examples/core/xx-move_on.rb
|
206
|
+
- examples/core/xx-mt-scheduler.rb
|
207
|
+
- examples/core/xx-queue-async.rb
|
208
|
+
- examples/core/xx-readpartial.rb
|
206
209
|
- examples/core/xx-recurrent-timer.rb
|
207
210
|
- examples/core/xx-resource_cancel.rb
|
208
211
|
- examples/core/xx-resource_delegate.rb
|
@@ -210,7 +213,10 @@ files:
|
|
210
213
|
- examples/core/xx-sleeping.rb
|
211
214
|
- examples/core/xx-spin_error_backtrace.rb
|
212
215
|
- examples/core/xx-supervisors.rb
|
213
|
-
- examples/core/xx-
|
216
|
+
- examples/core/xx-thread-selector-sleep.rb
|
217
|
+
- examples/core/xx-thread-selector-snooze.rb
|
218
|
+
- examples/core/xx-thread-sleep.rb
|
219
|
+
- examples/core/xx-thread-snooze.rb
|
214
220
|
- examples/core/xx-thread_pool.rb
|
215
221
|
- examples/core/xx-throttling.rb
|
216
222
|
- examples/core/xx-timeout.rb
|
@@ -241,11 +247,11 @@ files:
|
|
241
247
|
- examples/performance/multi_snooze.rb
|
242
248
|
- examples/performance/snooze.rb
|
243
249
|
- examples/performance/snooze_raw.rb
|
250
|
+
- examples/performance/thread-vs-fiber/polyphony_mt_server.rb
|
244
251
|
- examples/performance/thread-vs-fiber/polyphony_server.rb
|
245
252
|
- examples/performance/thread-vs-fiber/threaded_server.rb
|
246
253
|
- examples/performance/thread-vs-fiber/xx-httparty_multi.rb
|
247
254
|
- examples/performance/thread-vs-fiber/xx-httparty_threaded.rb
|
248
|
-
- examples/performance/thread.rb
|
249
255
|
- examples/performance/thread_pool_perf.rb
|
250
256
|
- ext/gyro/async.c
|
251
257
|
- ext/gyro/child.c
|
@@ -256,8 +262,11 @@ files:
|
|
256
262
|
- ext/gyro/io.c
|
257
263
|
- ext/gyro/libev.c
|
258
264
|
- ext/gyro/libev.h
|
265
|
+
- ext/gyro/queue.c
|
266
|
+
- ext/gyro/selector.c
|
259
267
|
- ext/gyro/signal.c
|
260
268
|
- ext/gyro/socket.c
|
269
|
+
- ext/gyro/thread.c
|
261
270
|
- ext/gyro/timer.c
|
262
271
|
- ext/libev/Changes
|
263
272
|
- ext/libev/LICENSE
|
@@ -283,7 +292,6 @@ files:
|
|
283
292
|
- lib/polyphony/core/resource_pool.rb
|
284
293
|
- lib/polyphony/core/supervisor.rb
|
285
294
|
- lib/polyphony/core/sync.rb
|
286
|
-
- lib/polyphony/core/thread.rb
|
287
295
|
- lib/polyphony/core/thread_pool.rb
|
288
296
|
- lib/polyphony/core/throttler.rb
|
289
297
|
- lib/polyphony/extensions/core.rb
|
@@ -291,6 +299,7 @@ files:
|
|
291
299
|
- lib/polyphony/extensions/io.rb
|
292
300
|
- lib/polyphony/extensions/openssl.rb
|
293
301
|
- lib/polyphony/extensions/socket.rb
|
302
|
+
- lib/polyphony/extensions/thread.rb
|
294
303
|
- lib/polyphony/fs.rb
|
295
304
|
- lib/polyphony/line_reader.rb
|
296
305
|
- lib/polyphony/net.rb
|
@@ -313,6 +322,8 @@ files:
|
|
313
322
|
- test/test_resource_pool.rb
|
314
323
|
- test/test_signal.rb
|
315
324
|
- test/test_supervisor.rb
|
325
|
+
- test/test_thread.rb
|
326
|
+
- test/test_throttler.rb
|
316
327
|
- test/test_timer.rb
|
317
328
|
homepage: https://dfab.gitbook.io/polyphony/
|
318
329
|
licenses:
|
@@ -339,7 +350,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
339
350
|
- !ruby/object:Gem::Version
|
340
351
|
version: '0'
|
341
352
|
requirements: []
|
342
|
-
rubygems_version: 3.
|
353
|
+
rubygems_version: 3.0.6
|
343
354
|
signing_key:
|
344
355
|
specification_version: 4
|
345
356
|
summary: 'Polyphony: Fiber-based Concurrency for Ruby'
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
5
|
-
|
6
|
-
@op_count = 0
|
7
|
-
|
8
|
-
def lengthy_op
|
9
|
-
100.times do
|
10
|
-
orig_sleep 0.01
|
11
|
-
@op_count += 1
|
12
|
-
end
|
13
|
-
@op_count
|
14
|
-
end
|
15
|
-
|
16
|
-
spin do
|
17
|
-
cancel_after(0.1) do
|
18
|
-
data = Polyphony::Thread.process { lengthy_op }
|
19
|
-
puts "slept #{data} times"
|
20
|
-
end
|
21
|
-
rescue Exception => e
|
22
|
-
puts "error: #{e}"
|
23
|
-
ensure
|
24
|
-
sleep 0.1
|
25
|
-
puts "slept #{@op_count} times"
|
26
|
-
end
|
27
|
-
|
28
|
-
suspend
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
5
|
-
|
6
|
-
def lengthy_op
|
7
|
-
10.times { IO.orig_read(__FILE__) }
|
8
|
-
end
|
9
|
-
|
10
|
-
X = 1000
|
11
|
-
|
12
|
-
def blocking
|
13
|
-
t0 = Time.now
|
14
|
-
data = lengthy_op
|
15
|
-
X.times { lengthy_op }
|
16
|
-
puts "read blocking (#{Time.now - t0}s)"
|
17
|
-
end
|
18
|
-
|
19
|
-
def threaded
|
20
|
-
t0 = Time.now
|
21
|
-
data = Polyphony::Thread.process { lengthy_op }
|
22
|
-
X.times { Polyphony::Thread.process { lengthy_op } }
|
23
|
-
puts "read threaded (#{Time.now - t0}s)"
|
24
|
-
end
|
25
|
-
|
26
|
-
blocking
|
27
|
-
threaded
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export :process
|
4
|
-
|
5
|
-
Exceptions = import('./exceptions')
|
6
|
-
|
7
|
-
def process(&block)
|
8
|
-
watcher = Gyro::Async.new
|
9
|
-
thread = Thread.new { run_in_thread(watcher, &block) }
|
10
|
-
watcher.await
|
11
|
-
ensure
|
12
|
-
thread.kill if thread.alive?
|
13
|
-
end
|
14
|
-
|
15
|
-
# Runs the given block, passing the result or exception to the given context
|
16
|
-
# @param ctx [Hash] context
|
17
|
-
# @return [void]
|
18
|
-
def run_in_thread(watcher)
|
19
|
-
result = yield
|
20
|
-
watcher.signal!(result)
|
21
|
-
rescue Exception => e
|
22
|
-
watcher.signal!(e)
|
23
|
-
end
|