polyphony 0.68 → 0.72

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/CHANGELOG.md +32 -5
  4. data/Gemfile.lock +2 -2
  5. data/TODO.md +1 -24
  6. data/bin/pdbg +1 -1
  7. data/bin/polyphony-debug +0 -0
  8. data/bin/stress.rb +0 -0
  9. data/bin/test +0 -0
  10. data/docs/_user-guide/all-about-timers.md +1 -1
  11. data/docs/api-reference/fiber.md +2 -2
  12. data/docs/faq.md +1 -1
  13. data/docs/getting-started/overview.md +8 -8
  14. data/docs/getting-started/tutorial.md +3 -3
  15. data/docs/main-concepts/concurrency.md +1 -1
  16. data/docs/main-concepts/extending.md +3 -3
  17. data/docs/main-concepts/fiber-scheduling.md +1 -1
  18. data/examples/core/calc.rb +37 -0
  19. data/examples/core/calc_with_restart.rb +40 -0
  20. data/examples/core/calc_with_supervise.rb +37 -0
  21. data/examples/core/message_based_supervision.rb +1 -1
  22. data/examples/io/rack_server.rb +1 -1
  23. data/examples/io/tunnel.rb +1 -1
  24. data/examples/performance/fiber_transfer.rb +1 -1
  25. data/examples/performance/line_splitting.rb +1 -1
  26. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  27. data/ext/polyphony/backend_common.c +24 -6
  28. data/ext/polyphony/backend_common.h +1 -0
  29. data/ext/polyphony/backend_io_uring.c +27 -40
  30. data/ext/polyphony/backend_io_uring_context.c +1 -1
  31. data/ext/polyphony/backend_io_uring_context.h +1 -1
  32. data/ext/polyphony/backend_libev.c +13 -11
  33. data/ext/polyphony/extconf.rb +24 -13
  34. data/ext/polyphony/queue.c +2 -2
  35. data/ext/polyphony/runqueue_ring_buffer.c +3 -2
  36. data/lib/polyphony/adapters/irb.rb +11 -1
  37. data/lib/polyphony/core/global_api.rb +3 -3
  38. data/lib/polyphony/core/timer.rb +2 -2
  39. data/lib/polyphony/debugger.rb +3 -3
  40. data/lib/polyphony/extensions/fiber.rb +30 -15
  41. data/lib/polyphony/extensions/io.rb +3 -3
  42. data/lib/polyphony/extensions/openssl.rb +21 -6
  43. data/lib/polyphony/extensions/socket.rb +4 -5
  44. data/lib/polyphony/net.rb +0 -1
  45. data/lib/polyphony/version.rb +1 -1
  46. data/polyphony.gemspec +1 -1
  47. data/test/coverage.rb +2 -2
  48. data/test/stress.rb +1 -1
  49. data/test/test_backend.rb +12 -12
  50. data/test/test_event.rb +1 -1
  51. data/test/test_ext.rb +1 -1
  52. data/test/test_fiber.rb +52 -12
  53. data/test/test_global_api.rb +2 -3
  54. data/test/test_io.rb +3 -3
  55. data/test/test_process_supervision.rb +38 -9
  56. data/test/test_queue.rb +6 -6
  57. data/test/test_socket.rb +12 -10
  58. data/test/test_supervise.rb +249 -81
  59. data/test/test_sync.rb +2 -2
  60. data/test/test_thread.rb +22 -2
  61. data/test/test_thread_pool.rb +1 -1
  62. data/test/test_throttler.rb +1 -1
  63. data/test/test_timer.rb +2 -2
  64. data/test/test_trace.rb +1 -1
  65. metadata +7 -3
@@ -3,12 +3,6 @@
3
3
  require_relative 'helper'
4
4
 
5
5
  class SuperviseTest < MiniTest::Test
6
- def test_supervise_with_no_arguments
7
- assert_raises(RuntimeError) do
8
- supervise
9
- end
10
- end
11
-
12
6
  def test_supervise_with_block
13
7
  buffer = []
14
8
  f1 = spin(:f1) { receive }
@@ -26,79 +20,253 @@ class SuperviseTest < MiniTest::Test
26
20
  assert_equal [[f1, 'foo'], [f2, 'bar']], buffer
27
21
  end
28
22
 
29
- # def test_supervise_with_restart
30
- # watcher = spin { receive }
31
- # parent = spin { supervise(restart: true, watcher: watcher) }
32
- # snooze
33
-
34
- # buffer = []
35
- # f1 = parent.spin do
36
- # buffer << 'f1'
37
- # end
38
-
39
- # f1.await
40
- # assert_equal ['f1'], buffer
41
- # watcher.await
42
- # assert_equal ['f1', 'f1'], buffer
43
- # end
44
-
45
- # def test_supervise_with_restart_on_error
46
- # parent = spin { supervise(restart: true) }
47
- # snooze
48
-
49
- # buffer = []
50
- # f1 = parent.spin do
51
- # buffer << 'f1'
52
- # buffer << receive
53
- # end
54
-
55
- # snooze
56
- # assert_equal ['f1'], buffer
57
-
58
- # f1.raise 'foo'
59
-
60
- # 3.times { snooze }
61
-
62
- # assert_equal ['f1', 'f1'], buffer
63
- # assert_equal :dead, f1.state
64
-
65
- # # f1 should have been restarted by supervisor
66
- # f1 = parent.children.first
67
- # assert_kind_of Fiber, f1
68
-
69
- # f1 << 'foo'
70
- # f1.await
71
-
72
- # assert_equal ['f1', 'f1', 'foo'], buffer
73
- # end
74
-
75
- # def test_supervisor_termination
76
- # f = nil
77
- # p = spin do
78
- # f = spin { sleep 1 }
79
- # supervise
80
- # end
81
- # sleep 0.01
82
-
83
- # p.terminate
84
- # p.await
85
-
86
- # assert :dead, f.state
87
- # assert :dead, p.state
88
- # end
89
-
90
- # def test_supervisor_termination_with_restart
91
- # f = nil
92
- # p = spin do
93
- # f = spin { sleep 1 }
94
- # supervise(restart: true)
95
- # end
96
- # sleep 0.01
97
-
98
- # p.terminate
99
- # p.await
100
-
101
- # assert :dead, f.state
102
- # assert :dead, p.state
103
- # end
23
+ def test_supervise_with_on_done
24
+ buffer = []
25
+ f1 = spin(:f1) { receive }
26
+ f2 = spin(:f2) { receive }
27
+ supervisor = spin(:supervisor) do
28
+ supervise(f1, f2, on_done: ->(*args) { buffer << args })
29
+ end
30
+
31
+ snooze
32
+ f1 << 'foo'
33
+ f1.await
34
+ 10.times { snooze }
35
+ assert_equal [[f1, 'foo']], buffer
36
+
37
+ f2 << 'bar'
38
+ f2.await
39
+ assert_equal [[f1, 'foo'], [f2, 'bar']], buffer
40
+ end
41
+
42
+ def test_supervise_with_on_error
43
+ buffer = []
44
+ f1 = spin(:f1) { receive }
45
+ f2 = spin(:f2) { receive }
46
+ supervisor = spin(:supervisor) do
47
+ supervise(f1, f2, on_error: ->(*args) { buffer << args })
48
+ end
49
+
50
+ snooze
51
+ f1 << 'foo'
52
+ f1.await
53
+ 10.times { snooze }
54
+ assert_equal [], buffer
55
+
56
+ e = RuntimeError.new('blah')
57
+ f2.raise(e)
58
+ 3.times { snooze }
59
+ assert_equal [[f2, e]], buffer
60
+ end
61
+
62
+ def test_supervise_with_manual_restart
63
+ buffer = []
64
+ f1 = spin(:f1) { receive }
65
+ supervisor = spin(:supervisor) do
66
+ supervise(f1) do |f, r|
67
+ buffer << [f, r]
68
+ f.restart
69
+ end
70
+ end
71
+
72
+ snooze
73
+ f1 << 'foo'
74
+ f1.await
75
+ snooze
76
+ assert_equal [[f1, 'foo']], buffer
77
+
78
+ 10.times { snooze }
79
+
80
+ assert_equal 1, supervisor.children.size
81
+ f2 = supervisor.children.first
82
+ assert f1 != f2
83
+ assert_equal :f1, f2.tag
84
+ assert_equal supervisor, f2.parent
85
+
86
+ e = RuntimeError.new('bar')
87
+ f2.raise(e)
88
+ f2.await rescue nil
89
+ 3.times { snooze }
90
+ assert_equal [[f1, 'foo'], [f2, e]], buffer
91
+
92
+ assert_equal 1, supervisor.children.size
93
+ f3 = supervisor.children.first
94
+ assert f2 != f3
95
+ assert f1 != f3
96
+ assert_equal :f1, f3.tag
97
+ assert_equal supervisor, f3.parent
98
+ end
99
+
100
+ def test_supervise_with_restart_always
101
+ buffer = []
102
+ f1 = spin(:f1) do
103
+ buffer << receive
104
+ rescue => e
105
+ buffer << e
106
+ e
107
+ end
108
+ supervisor = spin(:supervisor) { supervise(f1, restart: :always) }
109
+
110
+ snooze
111
+ f1 << 'foo'
112
+ f1.await
113
+ snooze
114
+ assert_equal ['foo'], buffer
115
+
116
+ 10.times { snooze }
117
+
118
+ assert_equal 1, supervisor.children.size
119
+ f2 = supervisor.children.first
120
+ assert f1 != f2
121
+ assert_equal :f1, f2.tag
122
+ assert_equal supervisor, f2.parent
123
+
124
+ e = RuntimeError.new('bar')
125
+ f2.raise(e)
126
+ f2.await rescue nil
127
+ 3.times { snooze }
128
+ assert_equal ['foo', e], buffer
129
+
130
+ assert_equal 1, supervisor.children.size
131
+ f3 = supervisor.children.first
132
+ assert f2 != f3
133
+ assert f1 != f3
134
+ assert_equal :f1, f3.tag
135
+ assert_equal supervisor, f3.parent
136
+ end
137
+
138
+ def test_supervise_with_restart_true
139
+ buffer = []
140
+ f1 = spin(:f1) do
141
+ buffer << receive
142
+ rescue => e
143
+ buffer << e
144
+ e
145
+ end
146
+ supervisor = spin(:supervisor) { supervise(f1, restart: true) }
147
+
148
+ snooze
149
+ f1 << 'foo'
150
+ f1.await
151
+ snooze
152
+ assert_equal ['foo'], buffer
153
+
154
+ 10.times { snooze }
155
+
156
+ assert_equal 1, supervisor.children.size
157
+ f2 = supervisor.children.first
158
+ assert f1 != f2
159
+ assert_equal :f1, f2.tag
160
+ assert_equal supervisor, f2.parent
161
+
162
+ e = RuntimeError.new('bar')
163
+ f2.raise(e)
164
+ f2.await rescue nil
165
+ 3.times { snooze }
166
+ assert_equal ['foo', e], buffer
167
+
168
+ assert_equal 1, supervisor.children.size
169
+ f3 = supervisor.children.first
170
+ assert f2 != f3
171
+ assert f1 != f3
172
+ assert_equal :f1, f3.tag
173
+ assert_equal supervisor, f3.parent
174
+ end
175
+
176
+ def test_supervise_with_restart_on_error
177
+ buffer = []
178
+ f1 = spin(:f1) do
179
+ buffer << receive
180
+ rescue => e
181
+ buffer << e
182
+ e
183
+ end
184
+ supervisor = spin(:supervisor) { supervise(f1, restart: :on_error) }
185
+
186
+ snooze
187
+ e = RuntimeError.new('bar')
188
+ f1.raise(e)
189
+ f1.await rescue nil
190
+ snooze
191
+ assert_equal [e], buffer
192
+
193
+ 10.times { snooze }
194
+
195
+ assert_equal 1, supervisor.children.size
196
+ f2 = supervisor.children.first
197
+ assert f1 != f2
198
+ assert_equal :f1, f2.tag
199
+ assert_equal supervisor, f2.parent
200
+
201
+ f2 << 'foo'
202
+ f2.await rescue nil
203
+ 3.times { snooze }
204
+ assert_equal [e, 'foo'], buffer
205
+
206
+ assert_equal 0, supervisor.children.size
207
+ end
208
+
209
+ def test_supervise_terminate
210
+ buffer = []
211
+ f1 = spin(:f1) do
212
+ buffer << receive
213
+ rescue => e
214
+ buffer << e
215
+ e
216
+ end
217
+ supervisor = spin(:supervisor) { supervise(f1, restart: :on_error) }
218
+
219
+ sleep 0.05
220
+ supervisor.terminate
221
+ supervisor.await
222
+
223
+ assert_equal [], buffer
224
+ end
225
+
226
+ def test_supervise_without_explicit_fibers
227
+ buffer = []
228
+ first = nil
229
+ supervisor = spin do
230
+ 3.times do |i|
231
+ f = spin do
232
+ first = Fiber.current if i == 0
233
+ receive
234
+ buffer << i
235
+ end
236
+ end
237
+ Fiber.current.parent << :ok
238
+ supervise(restart: :always)
239
+ end
240
+ msg = receive
241
+ assert_equal :ok, msg
242
+ assert_equal 3, supervisor.children.size
243
+
244
+ sleep 0.1
245
+ assert_equal [], buffer
246
+
247
+ old_first = first
248
+ first << :foo
249
+ first = nil
250
+ snooze
251
+ assert_equal [0], buffer
252
+
253
+ snooze
254
+ assert_equal 3, supervisor.children.size
255
+ snooze
256
+ assert first
257
+ assert first != old_first
258
+ end
259
+
260
+ def test_supervise_with_added_fibers
261
+ buffer = []
262
+ supervisor = spin do
263
+ supervise { |f, r| buffer << [f, r] }
264
+ end
265
+ snooze
266
+ f1 = supervisor.spin { snooze; :foo }
267
+ f2 = supervisor.spin { snooze; :bar }
268
+ Fiber.await(f1, f2)
269
+ snooze
270
+ assert_equal [[f1, :foo], [f2, :bar]], buffer
271
+ end
104
272
  end
data/test/test_sync.rb CHANGED
@@ -104,12 +104,12 @@ class MutexTest < MiniTest::Test
104
104
  snooze
105
105
  assert !lock.locked?
106
106
  a << Fiber.current
107
-
107
+
108
108
  receive
109
109
  assert lock.locked?
110
110
 
111
111
  a << Fiber.current
112
-
112
+
113
113
  receive
114
114
  assert !lock.locked?
115
115
  end
data/test/test_thread.rb CHANGED
@@ -180,7 +180,7 @@ class ThreadTest < MiniTest::Test
180
180
  assert_equal count, GC.count
181
181
  sleep 0.05
182
182
  assert_equal count, GC.count
183
-
183
+
184
184
  return unless IS_LINUX
185
185
 
186
186
  # The idle tasks are ran at most once per fiber switch, before the backend
@@ -207,7 +207,7 @@ class ThreadTest < MiniTest::Test
207
207
  counter = 0
208
208
 
209
209
  Thread.current.on_idle { counter += 1 }
210
-
210
+
211
211
  3.times { snooze }
212
212
  assert_equal 0, counter
213
213
 
@@ -220,4 +220,24 @@ class ThreadTest < MiniTest::Test
220
220
  3.times { snooze }
221
221
  assert_equal 2, counter
222
222
  end
223
+
224
+ def test_cross_thread_receive
225
+ buf = []
226
+ f = Fiber.current
227
+ t = Thread.new do
228
+ f << true
229
+ while (msg = receive)
230
+ buf << msg
231
+ end
232
+ end
233
+
234
+ receive # wait for thread to be ready
235
+ t << 1
236
+ t << 2
237
+ t << 3
238
+ t << nil
239
+
240
+ t.join
241
+ assert_equal [1, 2, 3], buf
242
+ end
223
243
  end
@@ -67,7 +67,7 @@ class ThreadPoolTest < MiniTest::Test
67
67
 
68
68
  assert_in_range 0.0..0.009, elapsed
69
69
  assert buffer.size < 2
70
-
70
+
71
71
  sleep 0.15 # allow time for threads to spawn
72
72
  assert_equal @pool.size, threads.uniq.size
73
73
  assert_equal (0..9).to_a, buffer.sort if IS_LINUX
@@ -34,7 +34,7 @@ class ThrottlerTest < MiniTest::Test
34
34
  f = spin { loop { t.process { buffer << 1 } } }
35
35
  sleep 0.02
36
36
  f.stop
37
- assert_in_range 2..3, buffer.size
37
+ assert_in_range 2..4, buffer.size
38
38
  ensure
39
39
  t.stop
40
40
  end
data/test/test_timer.rb CHANGED
@@ -137,11 +137,11 @@ class TimerMiscTest < MiniTest::Test
137
137
  @timer = Polyphony::Timer.new(resolution: 0.001)
138
138
  sleep 0
139
139
  end
140
-
140
+
141
141
  def teardown
142
142
  @timer.stop
143
143
  end
144
-
144
+
145
145
  def test_timer_after
146
146
  buffer = []
147
147
  f = @timer.after(0.01) { buffer << 2 }
data/test/test_trace.rb CHANGED
@@ -7,7 +7,7 @@ class TraceTest < MiniTest::Test
7
7
  events = []
8
8
  Thread.backend.trace_proc = proc { |*e| events << e }
9
9
  snooze
10
-
10
+
11
11
  assert_equal [
12
12
  [:fiber_schedule, Fiber.current, nil, false],
13
13
  [:fiber_switchpoint, Fiber.current],
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.68'
4
+ version: '0.72'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-13 00:00:00.000000000 Z
11
+ date: 2021-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -144,6 +144,7 @@ extensions:
144
144
  extra_rdoc_files:
145
145
  - README.md
146
146
  files:
147
+ - ".github/FUNDING.yml"
147
148
  - ".github/workflows/test.yml"
148
149
  - ".gitignore"
149
150
  - ".gitmodules"
@@ -215,6 +216,9 @@ files:
215
216
  - examples/adapters/sequel_mysql_pool.rb
216
217
  - examples/adapters/sequel_pg.rb
217
218
  - examples/core/await.rb
219
+ - examples/core/calc.rb
220
+ - examples/core/calc_with_restart.rb
221
+ - examples/core/calc_with_supervise.rb
218
222
  - examples/core/channels.rb
219
223
  - examples/core/deferring-an-operation.rb
220
224
  - examples/core/enumerable.rb
@@ -421,7 +425,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
421
425
  - !ruby/object:Gem::Version
422
426
  version: '0'
423
427
  requirements: []
424
- rubygems_version: 3.1.4
428
+ rubygems_version: 3.1.2
425
429
  signing_key:
426
430
  specification_version: 4
427
431
  summary: Fine grained concurrency for Ruby