polyphony 0.43.2 → 0.43.8

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -2
  3. data/CHANGELOG.md +43 -0
  4. data/Gemfile.lock +2 -2
  5. data/README.md +2 -0
  6. data/TODO.md +2 -3
  7. data/docs/_includes/head.html +40 -0
  8. data/docs/_includes/title.html +1 -0
  9. data/docs/_user-guide/web-server.md +11 -11
  10. data/docs/getting-started/overview.md +4 -4
  11. data/docs/index.md +4 -3
  12. data/docs/main-concepts/design-principles.md +23 -34
  13. data/docs/main-concepts/fiber-scheduling.md +1 -1
  14. data/docs/polyphony-logo.png +0 -0
  15. data/examples/adapters/concurrent-ruby.rb +9 -0
  16. data/examples/core/xx-daemon.rb +14 -0
  17. data/examples/io/xx-happy-eyeballs.rb +21 -22
  18. data/examples/io/xx-zip.rb +19 -0
  19. data/examples/performance/fiber_transfer.rb +47 -0
  20. data/examples/performance/mem-usage.rb +34 -28
  21. data/examples/performance/messaging.rb +29 -0
  22. data/examples/performance/multi_snooze.rb +11 -9
  23. data/examples/xx-spin.rb +32 -0
  24. data/ext/polyphony/libev_agent.c +181 -24
  25. data/ext/polyphony/polyphony.c +0 -2
  26. data/ext/polyphony/polyphony.h +14 -7
  27. data/ext/polyphony/polyphony_ext.c +2 -2
  28. data/ext/polyphony/queue.c +168 -0
  29. data/ext/polyphony/ring_buffer.c +96 -0
  30. data/ext/polyphony/ring_buffer.h +28 -0
  31. data/ext/polyphony/thread.c +16 -8
  32. data/lib/polyphony.rb +28 -12
  33. data/lib/polyphony/core/global_api.rb +5 -3
  34. data/lib/polyphony/core/resource_pool.rb +19 -9
  35. data/lib/polyphony/core/thread_pool.rb +1 -1
  36. data/lib/polyphony/event.rb +5 -15
  37. data/lib/polyphony/extensions/core.rb +40 -0
  38. data/lib/polyphony/extensions/fiber.rb +9 -14
  39. data/lib/polyphony/extensions/io.rb +17 -16
  40. data/lib/polyphony/extensions/openssl.rb +8 -0
  41. data/lib/polyphony/extensions/socket.rb +12 -0
  42. data/lib/polyphony/version.rb +1 -1
  43. data/test/helper.rb +1 -1
  44. data/test/q.rb +24 -0
  45. data/test/test_agent.rb +3 -3
  46. data/test/test_event.rb +11 -0
  47. data/test/test_fiber.rb +3 -3
  48. data/test/test_global_api.rb +48 -15
  49. data/test/test_io.rb +24 -2
  50. data/test/test_queue.rb +39 -1
  51. data/test/test_resource_pool.rb +12 -0
  52. data/test/test_throttler.rb +6 -5
  53. data/test/test_trace.rb +18 -17
  54. metadata +15 -4
  55. data/ext/polyphony/libev_queue.c +0 -217
@@ -45,4 +45,15 @@ class EventTest < MiniTest::Test
45
45
  t&.kill
46
46
  t&.join
47
47
  end
48
+
49
+ def test_exception_while_waiting_for_event
50
+ e = Polyphony::Event.new
51
+
52
+ f = spin { e.await }
53
+ g = spin { f.raise 'foo' }
54
+
55
+ assert_raises(RuntimeError) do
56
+ f.await
57
+ end
58
+ end
48
59
  end
@@ -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
 
@@ -187,13 +211,13 @@ class SpinLoopTest < MiniTest::Test
187
211
 
188
212
  def test_spin_loop_location
189
213
  location = /^#{__FILE__}:#{__LINE__ + 1}/
190
- f = spin_loop {}
214
+ f = spin_loop { snooze }
191
215
 
192
216
  assert_match location, f.location
193
217
  end
194
218
 
195
219
  def test_spin_loop_tag
196
- f = spin_loop(:my_loop) {}
220
+ f = spin_loop(:my_loop) { snooze }
197
221
 
198
222
  assert_equal :my_loop, f.tag
199
223
  end
@@ -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
@@ -30,6 +30,14 @@ class IOTest < MiniTest::Test
30
30
  assert_equal 'hello', msg
31
31
  end
32
32
 
33
+ def test_write_multiple_arguments
34
+ i, o = IO.pipe
35
+ count = o.write('a', 'b', "\n", 'c')
36
+ assert_equal 4, count
37
+ o.close
38
+ assert_equal "ab\nc", i.read
39
+ end
40
+
33
41
  def test_that_double_chevron_method_returns_io
34
42
  assert_equal @o, @o << 'foo'
35
43
 
@@ -83,6 +91,20 @@ class IOTest < MiniTest::Test
83
91
 
84
92
  assert_raises(EOFError) { i.readpartial(1) }
85
93
  end
94
+
95
+ # see https://github.com/digital-fabric/polyphony/issues/30
96
+ def test_reopened_tempfile
97
+ file = Tempfile.new
98
+ file << 'hello: world'
99
+ file.close
100
+
101
+ buf = nil
102
+ File.open(file, 'r:bom|utf-8') do |f|
103
+ buf = f.read(16384)
104
+ end
105
+
106
+ assert_equal 'hello: world', buf
107
+ end
86
108
  end
87
109
 
88
110
  class IOClassMethodsTest < MiniTest::Test
@@ -121,7 +143,7 @@ class IOClassMethodsTest < MiniTest::Test
121
143
  assert_equal "end\n", lines[-1]
122
144
  end
123
145
 
124
- def test_read
146
+ def test_read_class_method
125
147
  s = IO.read(__FILE__)
126
148
  assert_kind_of String, s
127
149
  assert(!s.empty?)
@@ -144,7 +166,7 @@ class IOClassMethodsTest < MiniTest::Test
144
166
 
145
167
  WRITE_DATA = "foo\nbar קוקו"
146
168
 
147
- def test_write
169
+ def test_write_class_method
148
170
  fn = '/tmp/test_write'
149
171
  FileUtils.rm(fn) rescue nil
150
172
 
@@ -8,7 +8,7 @@ class QueueTest < MiniTest::Test
8
8
  @queue = Polyphony::Queue.new
9
9
  end
10
10
 
11
- def test_pop
11
+ def test_push_shift
12
12
  spin {
13
13
  @queue << 42
14
14
  }
@@ -21,6 +21,18 @@ class QueueTest < MiniTest::Test
21
21
  assert_equal [1, 2, 3, 4], buf
22
22
  end
23
23
 
24
+ def test_unshift
25
+ @queue.push 1
26
+ @queue.push 2
27
+ @queue.push 3
28
+ @queue.unshift 4
29
+
30
+ buf = []
31
+ buf << @queue.shift while !@queue.empty?
32
+
33
+ assert_equal [4, 1, 2, 3], buf
34
+ end
35
+
24
36
  def test_multiple_waiters
25
37
  a = spin { @queue.shift }
26
38
  b = spin { @queue.shift }
@@ -41,6 +53,19 @@ class QueueTest < MiniTest::Test
41
53
  buf = []
42
54
  @queue.shift_each { |i| buf << i }
43
55
  assert_equal [1, 2, 3, 4], buf
56
+
57
+ buf = []
58
+ @queue.shift_each { |i| buf << i }
59
+ assert_equal [], buf
60
+ end
61
+
62
+ def test_shift_all
63
+ (1..4).each { |i| @queue << i }
64
+ buf = @queue.shift_all
65
+ assert_equal [1, 2, 3, 4], buf
66
+
67
+ buf = @queue.shift_all
68
+ assert_equal [], buf
44
69
  end
45
70
 
46
71
  def test_empty?
@@ -71,4 +96,17 @@ class QueueTest < MiniTest::Test
71
96
  assert_nil f2.await
72
97
  assert_equal :bar, f3.await
73
98
  end
99
+
100
+ def test_fiber_removal_from_queue_simple
101
+ f1 = spin { @queue.shift }
102
+
103
+ # let fibers run
104
+ snooze
105
+
106
+ f1.stop
107
+ snooze
108
+
109
+ @queue << :foo
110
+ assert_nil f1.await
111
+ end
74
112
  end
@@ -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
@@ -34,7 +34,8 @@ class TraceTest < MiniTest::Test
34
34
 
35
35
  def test_2_fiber_trace
36
36
  records = []
37
- t = Polyphony::Trace.new(:fiber_all) { |r| records << r if r[:event] =~ /^fiber_/ }
37
+ thread = Thread.current
38
+ t = Polyphony::Trace.new(:fiber_all) { |r| records << r if Thread.current == thread && r[:event] =~ /^fiber_/ }
38
39
  t.enable
39
40
  Polyphony.trace(true)
40
41
 
@@ -42,23 +43,23 @@ class TraceTest < MiniTest::Test
42
43
  suspend
43
44
  sleep 0
44
45
 
45
- events = records.map { |r| [r[:fiber], r[:event]] }
46
+ events = records.map { |r| [r[:fiber] == f ? :f : :current, r[:event]] }
46
47
  assert_equal [
47
- [f, :fiber_create],
48
- [f, :fiber_schedule],
49
- [Fiber.current, :fiber_switchpoint],
50
- [f, :fiber_run],
51
- [f, :fiber_switchpoint],
52
- [f, :fiber_ev_loop_enter],
53
- [f, :fiber_schedule],
54
- [f, :fiber_ev_loop_leave],
55
- [f, :fiber_run],
56
- [f, :fiber_terminate],
57
- [Fiber.current, :fiber_switchpoint],
58
- [Fiber.current, :fiber_ev_loop_enter],
59
- [Fiber.current, :fiber_schedule],
60
- [Fiber.current, :fiber_ev_loop_leave],
61
- [Fiber.current, :fiber_run]
48
+ [:f, :fiber_create],
49
+ [:f, :fiber_schedule],
50
+ [:current, :fiber_switchpoint],
51
+ [:f, :fiber_run],
52
+ [:f, :fiber_switchpoint],
53
+ [:f, :fiber_ev_loop_enter],
54
+ [:f, :fiber_schedule],
55
+ [:f, :fiber_ev_loop_leave],
56
+ [:f, :fiber_run],
57
+ [:f, :fiber_terminate],
58
+ [:current, :fiber_switchpoint],
59
+ [:current, :fiber_ev_loop_enter],
60
+ [:current, :fiber_schedule],
61
+ [:current, :fiber_ev_loop_leave],
62
+ [:current, :fiber_run]
62
63
  ], events
63
64
  ensure
64
65
  t&.disable
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.8
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-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -242,6 +242,8 @@ files:
242
242
  - TODO.md
243
243
  - bin/polyphony-debug
244
244
  - docs/_config.yml
245
+ - docs/_includes/head.html
246
+ - docs/_includes/title.html
245
247
  - docs/_sass/custom/custom.scss
246
248
  - docs/_sass/overrides.scss
247
249
  - docs/_user-guide/all-about-timers.md
@@ -280,6 +282,7 @@ files:
280
282
  - docs/main-concepts/fiber-scheduling.md
281
283
  - docs/main-concepts/index.md
282
284
  - docs/polyphony-logo.png
285
+ - examples/adapters/concurrent-ruby.rb
283
286
  - examples/adapters/pg_client.rb
284
287
  - examples/adapters/pg_notify.rb
285
288
  - examples/adapters/pg_pool.rb
@@ -297,6 +300,7 @@ files:
297
300
  - examples/core/xx-at_exit.rb
298
301
  - examples/core/xx-caller.rb
299
302
  - examples/core/xx-channels.rb
303
+ - examples/core/xx-daemon.rb
300
304
  - examples/core/xx-deadlock.rb
301
305
  - examples/core/xx-deferring-an-operation.rb
302
306
  - examples/core/xx-erlang-style-genserver.rb
@@ -349,8 +353,11 @@ files:
349
353
  - examples/io/xx-system.rb
350
354
  - examples/io/xx-tcpserver.rb
351
355
  - examples/io/xx-tcpsocket.rb
356
+ - examples/io/xx-zip.rb
357
+ - examples/performance/fiber_transfer.rb
352
358
  - examples/performance/fs_read.rb
353
359
  - examples/performance/mem-usage.rb
360
+ - examples/performance/messaging.rb
354
361
  - examples/performance/multi_snooze.rb
355
362
  - examples/performance/snooze.rb
356
363
  - examples/performance/snooze_raw.rb
@@ -364,6 +371,7 @@ files:
364
371
  - examples/performance/xx-array.rb
365
372
  - examples/performance/xx-fiber-switch.rb
366
373
  - examples/performance/xx-snooze.rb
374
+ - examples/xx-spin.rb
367
375
  - ext/libev/Changes
368
376
  - ext/libev/LICENSE
369
377
  - ext/libev/README
@@ -385,10 +393,12 @@ files:
385
393
  - ext/polyphony/libev.c
386
394
  - ext/polyphony/libev.h
387
395
  - ext/polyphony/libev_agent.c
388
- - ext/polyphony/libev_queue.c
389
396
  - ext/polyphony/polyphony.c
390
397
  - ext/polyphony/polyphony.h
391
398
  - ext/polyphony/polyphony_ext.c
399
+ - ext/polyphony/queue.c
400
+ - ext/polyphony/ring_buffer.c
401
+ - ext/polyphony/ring_buffer.h
392
402
  - ext/polyphony/thread.c
393
403
  - ext/polyphony/tracing.c
394
404
  - lib/polyphony.rb
@@ -418,6 +428,7 @@ files:
418
428
  - test/coverage.rb
419
429
  - test/eg.rb
420
430
  - test/helper.rb
431
+ - test/q.rb
421
432
  - test/run.rb
422
433
  - test/stress.rb
423
434
  - test/test_agent.rb
@@ -464,7 +475,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
464
475
  - !ruby/object:Gem::Version
465
476
  version: '0'
466
477
  requirements: []
467
- rubygems_version: 3.0.6
478
+ rubygems_version: 3.1.2
468
479
  signing_key:
469
480
  specification_version: 4
470
481
  summary: Fine grained concurrency for Ruby