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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -3
  3. data/CHANGELOG.md +8 -0
  4. data/Gemfile.lock +1 -1
  5. data/TODO.md +34 -14
  6. data/examples/core/xx-mt-scheduler.rb +349 -0
  7. data/examples/core/xx-queue-async.rb +120 -0
  8. data/examples/core/xx-readpartial.rb +18 -0
  9. data/examples/core/xx-thread-selector-sleep.rb +51 -0
  10. data/examples/core/xx-thread-selector-snooze.rb +46 -0
  11. data/examples/core/xx-thread-sleep.rb +17 -0
  12. data/examples/core/xx-thread-snooze.rb +34 -0
  13. data/examples/performance/snooze.rb +5 -5
  14. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +73 -0
  15. data/examples/performance/thread-vs-fiber/polyphony_server.rb +12 -4
  16. data/ext/gyro/async.c +58 -61
  17. data/ext/gyro/child.c +50 -59
  18. data/ext/gyro/gyro.c +84 -192
  19. data/ext/gyro/gyro.h +29 -2
  20. data/ext/gyro/gyro_ext.c +6 -0
  21. data/ext/gyro/io.c +87 -96
  22. data/ext/gyro/queue.c +109 -0
  23. data/ext/gyro/selector.c +117 -0
  24. data/ext/gyro/signal.c +44 -54
  25. data/ext/gyro/socket.c +15 -20
  26. data/ext/gyro/thread.c +203 -0
  27. data/ext/gyro/timer.c +79 -50
  28. data/ext/libev/ev.c +3 -0
  29. data/lib/polyphony.rb +7 -12
  30. data/lib/polyphony/core/global_api.rb +5 -1
  31. data/lib/polyphony/core/throttler.rb +6 -11
  32. data/lib/polyphony/extensions/core.rb +2 -0
  33. data/lib/polyphony/extensions/fiber.rb +11 -13
  34. data/lib/polyphony/extensions/thread.rb +52 -0
  35. data/lib/polyphony/version.rb +1 -1
  36. data/test/helper.rb +8 -3
  37. data/test/test_fiber.rb +2 -2
  38. data/test/test_global_api.rb +4 -5
  39. data/test/test_gyro.rb +3 -2
  40. data/test/test_io.rb +1 -0
  41. data/test/test_supervisor.rb +3 -3
  42. data/test/test_thread.rb +44 -0
  43. data/test/test_throttler.rb +41 -0
  44. data/test/test_timer.rb +13 -7
  45. metadata +17 -6
  46. data/examples/core/xx-thread_cancel.rb +0 -28
  47. data/examples/performance/thread.rb +0 -27
  48. data/lib/polyphony/core/thread.rb +0 -23
@@ -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.02; buffer << :bar; :bar }
331
- f3 = spin { sleep 0.03; buffer << :baz; :baz }
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
@@ -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
- Gyro.run
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
- Gyro.run
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
- assert_equal [1, 2, 3, 4, 5], buffer
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
- puts "* elapsed: #{elapsed.inspect}"
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
@@ -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
- Gyro.reset!
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
- Gyro.reset!
165
+ Thread.current.reset_fiber_scheduling
165
166
 
166
167
  # control is transfer to the fiber that called Gyro.restart
167
168
  values << :restarted
@@ -28,6 +28,7 @@ end
28
28
 
29
29
  class IOTest < MiniTest::Test
30
30
  def setup
31
+ super
31
32
  @i, @o = IO.pipe
32
33
  end
33
34
 
@@ -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.01; buffer << :foo; :foo }
59
- bar_f = s.spin { sleep 0.03; buffer << :bar; :bar }
60
- baz_f = s.spin { sleep 0.05; buffer << :baz; :baz }
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
@@ -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
@@ -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.01, 0.01)
34
- times = []
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
- times << ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
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.005
43
- break if count >= 10
47
+ sleep 0.05
48
+ break if count >= 3
44
49
  }
45
50
  }
46
51
  suspend
47
- deltas = times.each_with_object([]) { |t, a| a << t - last; last = t }
48
- assert_equal 0, deltas.filter { |d| (d - 0.01).abs >= 0.006 }.size
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.26'
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-12 00:00:00.000000000 Z
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-thread_cancel.rb
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.1.2
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