polyphony 0.68 → 0.72

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