polyphony 0.26 → 0.27
Sign up to get free protection for your applications and to get access to all the features.
- 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
|