polyphony 0.24 → 0.25

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/TODO.md +12 -8
  5. data/docs/README.md +2 -2
  6. data/docs/summary.md +3 -3
  7. data/docs/technical-overview/concurrency.md +4 -6
  8. data/docs/technical-overview/design-principles.md +8 -8
  9. data/docs/technical-overview/exception-handling.md +1 -1
  10. data/examples/core/{01-spinning-up-coprocesses.rb → 01-spinning-up-fibers.rb} +1 -1
  11. data/examples/core/{02-awaiting-coprocesses.rb → 02-awaiting-fibers.rb} +3 -3
  12. data/examples/core/xx-erlang-style-genserver.rb +10 -10
  13. data/examples/core/xx-extended_fibers.rb +150 -0
  14. data/examples/core/xx-sleeping.rb +9 -0
  15. data/examples/core/xx-supervisors.rb +1 -1
  16. data/examples/interfaces/pg_pool.rb +3 -3
  17. data/examples/performance/mem-usage.rb +19 -4
  18. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -5
  19. data/ext/gyro/gyro.c +9 -15
  20. data/lib/polyphony/core/cancel_scope.rb +0 -2
  21. data/lib/polyphony/core/exceptions.rb +2 -2
  22. data/lib/polyphony/core/global_api.rb +7 -8
  23. data/lib/polyphony/core/supervisor.rb +25 -31
  24. data/lib/polyphony/extensions/core.rb +4 -78
  25. data/lib/polyphony/extensions/fiber.rb +166 -0
  26. data/lib/polyphony/extensions/io.rb +2 -1
  27. data/lib/polyphony/version.rb +1 -1
  28. data/lib/polyphony.rb +6 -8
  29. data/test/test_async.rb +2 -2
  30. data/test/test_cancel_scope.rb +6 -6
  31. data/test/test_fiber.rb +382 -0
  32. data/test/test_global_api.rb +49 -50
  33. data/test/test_gyro.rb +1 -1
  34. data/test/test_io.rb +30 -29
  35. data/test/test_kernel.rb +2 -2
  36. data/test/test_signal.rb +1 -1
  37. data/test/test_supervisor.rb +27 -27
  38. data/test/test_timer.rb +2 -2
  39. metadata +7 -7
  40. data/examples/core/04-no-auto-run.rb +0 -16
  41. data/lib/polyphony/core/coprocess.rb +0 -168
  42. data/test/test_coprocess.rb +0 -440
@@ -1,440 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- class CoprocessTest < MiniTest::Test
6
- def test_that_root_fiber_has_associated_coprocess
7
- assert_equal(Fiber.current, Polyphony::Coprocess.current.fiber)
8
- assert_equal(Polyphony::Coprocess.current, Fiber.current.coprocess)
9
- end
10
-
11
- def test_that_new_coprocess_starts_in_suspended_state
12
- result = nil
13
- coproc = Polyphony::Coprocess.new { result = 42 }
14
- assert_nil(result)
15
- coproc.await
16
- assert_equal(42, result)
17
- ensure
18
- coproc&.stop
19
- end
20
-
21
- def test_that_new_coprocess_runs_on_different_fiber
22
- coproc = Polyphony::Coprocess.new { Fiber.current }
23
- fiber = coproc.await
24
- assert(fiber != Fiber.current)
25
- ensure
26
- coproc&.stop
27
- end
28
-
29
- def test_that_await_blocks_until_coprocess_is_done
30
- result = nil
31
- coproc = Polyphony::Coprocess.new do
32
- snooze
33
- result = 42
34
- end
35
- coproc.await
36
- assert_equal(42, result)
37
- ensure
38
- coproc&.stop
39
- end
40
-
41
- def test_that_await_returns_the_coprocess_return_value
42
- coproc = Polyphony::Coprocess.new { %i[foo bar] }
43
- assert_equal(%i[foo bar], coproc.await)
44
- ensure
45
- coproc&.stop
46
- end
47
-
48
- def test_that_await_raises_error_raised_by_coprocess
49
- result = nil
50
- coproc = Polyphony::Coprocess.new { raise 'foo' }
51
- begin
52
- result = coproc.await
53
- rescue Exception => e
54
- result = { error: e }
55
- end
56
- assert_kind_of(Hash, result)
57
- assert_kind_of(RuntimeError, result[:error])
58
- ensure
59
- coproc&.stop
60
- end
61
-
62
- def test_that_running_coprocess_can_be_cancelled
63
- result = []
64
- error = nil
65
- coproc = Polyphony::Coprocess.new do
66
- result << 1
67
- 2.times { snooze }
68
- result << 2
69
- end.run
70
- defer { coproc.cancel! }
71
- assert_equal(0, result.size)
72
- begin
73
- coproc.await
74
- rescue Polyphony::Cancel => e
75
- error = e
76
- end
77
- assert_equal(1, result.size)
78
- assert_equal(1, result[0])
79
- assert_kind_of(Polyphony::Cancel, error)
80
- ensure
81
- coproc&.stop
82
- end
83
-
84
- def test_that_running_coprocess_can_be_interrupted
85
- # that is, stopped without exception
86
- result = []
87
- coproc = Polyphony::Coprocess.new do
88
- result << 1
89
- 2.times { snooze }
90
- result << 2
91
- 3
92
- end.run
93
- defer { coproc.stop(42) }
94
-
95
- await_result = coproc.await
96
- assert_equal(1, result.size)
97
- assert_equal(42, await_result)
98
- ensure
99
- coproc&.stop
100
- end
101
-
102
- def test_that_coprocess_can_be_awaited
103
- result = nil
104
- cp2 = nil
105
- cp1 = spin do
106
- cp2 = Polyphony::Coprocess.new do
107
- snooze
108
- 42
109
- end
110
- result = cp2.await
111
- end
112
- suspend
113
- assert_equal(42, result)
114
- ensure
115
- cp1&.stop
116
- cp2&.stop
117
- end
118
-
119
- def test_that_coprocess_can_be_stopped
120
- result = nil
121
- coproc = spin do
122
- snooze
123
- result = 42
124
- end
125
- defer { coproc.interrupt }
126
- suspend
127
- assert_nil(result)
128
- ensure
129
- coproc&.stop
130
- end
131
-
132
- def test_that_coprocess_can_be_cancelled
133
- result = nil
134
- coproc = spin do
135
- snooze
136
- result = 42
137
- rescue Polyphony::Cancel => e
138
- result = e
139
- end
140
- defer { coproc.cancel! }
141
-
142
- suspend
143
-
144
- assert_kind_of(Polyphony::Cancel, result)
145
- assert_kind_of(Polyphony::Cancel, coproc.result)
146
- assert_nil(coproc.alive?)
147
- ensure
148
- coproc&.stop
149
- end
150
-
151
- def test_that_inner_coprocess_can_be_interrupted
152
- result = nil
153
- cp2 = nil
154
- cp1 = spin do
155
- cp2 = spin do
156
- snooze
157
- result = 42
158
- end
159
- cp2.await
160
- result && result += 1
161
- end
162
- defer { cp1.interrupt }
163
- suspend
164
- assert_nil(result)
165
- assert_nil(cp1.alive?)
166
- assert_nil(cp2.alive?)
167
- ensure
168
- cp1&.stop
169
- cp2&.stop
170
- end
171
-
172
- def test_that_inner_coprocess_can_interrupt_outer_coprocess
173
- result, cp2 = nil
174
-
175
- cp1 = spin do
176
- cp2 = spin do
177
- defer { cp1.interrupt }
178
- snooze
179
- snooze
180
- result = 42
181
- end
182
- cp2.await
183
- result && result += 1
184
- end
185
-
186
- suspend
187
-
188
- assert_nil(result)
189
- assert_nil(cp1.alive?)
190
- assert_nil(cp2.alive?)
191
- ensure
192
- cp1&.stop
193
- cp2&.stop
194
- end
195
-
196
- def test_alive?
197
- counter = 0
198
- coproc = spin do
199
- 3.times do
200
- snooze
201
- counter += 1
202
- end
203
- end
204
-
205
- assert(coproc.alive?)
206
- snooze
207
- assert(coproc.alive?)
208
- snooze while counter < 3
209
- assert(!coproc.alive?)
210
- ensure
211
- coproc&.stop
212
- end
213
-
214
- def test_coprocess_exception_propagation
215
- # error is propagated to calling coprocess
216
- raised_error = nil
217
- spin do
218
- spin do
219
- raise 'foo'
220
- end
221
- snooze # allow nested coprocess to run before finishing
222
- end
223
- suspend
224
- rescue Exception => e
225
- raised_error = e
226
- ensure
227
- assert(raised_error)
228
- assert_equal('foo', raised_error.message)
229
- end
230
-
231
- def test_that_coprocess_can_be_cancelled_before_first_scheduling
232
- buffer = []
233
- coproc = spin { buffer << 1 }
234
- coproc.stop
235
-
236
- snooze
237
- assert_nil coproc.alive?
238
- assert_equal [], buffer
239
- end
240
-
241
- def test_exception_propagation_for_orphan_fiber
242
- raised_error = nil
243
- spin do
244
- spin do
245
- snooze
246
- raise 'bar'
247
- end
248
- end
249
- suspend
250
- rescue Exception => e
251
- raised_error = e
252
- ensure
253
- assert(raised_error)
254
- assert_equal('bar', raised_error.message)
255
- end
256
-
257
- def test_await_multiple_coprocesses
258
- cp1 = spin { sleep 0.01; :foo }
259
- cp2 = spin { sleep 0.01; :bar }
260
- cp3 = spin { sleep 0.01; :baz }
261
-
262
- result = Polyphony::Coprocess.await(cp1, cp2, cp3)
263
- assert_equal %i{foo bar baz}, result
264
- end
265
-
266
- def test_join_multiple_coprocesses
267
- cp1 = spin { sleep 0.01; :foo }
268
- cp2 = spin { sleep 0.01; :bar }
269
- cp3 = spin { sleep 0.01; :baz }
270
-
271
- result = Polyphony::Coprocess.join(cp1, cp2, cp3)
272
- assert_equal %i{foo bar baz}, result
273
- end
274
-
275
- def test_caller
276
- location = /^#{__FILE__}:#{__LINE__ + 1}/
277
- cp = spin do
278
- sleep 0.01
279
- end
280
- snooze
281
-
282
- caller = cp.caller
283
- assert_match location, caller[0]
284
- end
285
-
286
- def test_location
287
- location = /^#{__FILE__}:#{__LINE__ + 1}/
288
- cp = spin do
289
- sleep 0.01
290
- end
291
- snooze
292
-
293
- assert cp.location =~ location
294
- end
295
-
296
- def test_when_done
297
- flag = nil
298
- values = []
299
- coproc = spin do
300
- snooze until flag
301
- end
302
- coproc.when_done { values << 42 }
303
-
304
- snooze
305
- assert values.empty?
306
- snooze
307
- flag = true
308
- assert values.empty?
309
- assert coproc.alive?
310
-
311
- snooze
312
- assert_equal [42], values
313
- assert !coproc.alive?
314
- end
315
-
316
- def test_resume
317
- values = []
318
- coproc = spin do
319
- values << 1
320
- x = suspend
321
- values << x
322
- suspend
323
- values << 3
324
- end
325
- snooze
326
- assert_equal [1], values
327
-
328
- coproc.resume 2
329
- assert_equal [1, 2], values
330
-
331
- coproc.resume
332
- assert_equal [1, 2, 3], values
333
-
334
- assert !coproc.alive?
335
- end
336
-
337
- def test_interrupt
338
- coproc = spin do
339
- sleep 1
340
- :foo
341
- end
342
-
343
- snooze
344
- assert coproc.alive?
345
-
346
- coproc.interrupt :bar
347
- assert !coproc.alive?
348
-
349
- assert_equal :bar, coproc.result
350
- end
351
-
352
- def test_cancel
353
- error = nil
354
- coproc = spin do
355
- sleep 1
356
- :foo
357
- end
358
-
359
- snooze
360
- coproc.cancel!
361
- rescue Polyphony::Cancel => e
362
- # cancel error should bubble up
363
- error = e
364
- ensure
365
- assert error
366
- assert !coproc.alive?
367
- end
368
-
369
- def test_current
370
- assert_equal Fiber.root.coprocess, Polyphony::Coprocess.current
371
-
372
- value = nil
373
- coproc = spin do
374
- value = :ok if Polyphony::Coprocess.current == coproc
375
- end
376
-
377
- snooze
378
- assert_equal :ok, value
379
- end
380
- end
381
-
382
- class MailboxTest < MiniTest::Test
383
- def test_that_coprocess_can_receive_messages
384
- msgs = []
385
- coproc = spin { loop { msgs << receive } }
386
-
387
- snooze # allow coproc to start
388
-
389
- 3.times do |i|
390
- coproc << i
391
- snooze
392
- end
393
-
394
- assert_equal([0, 1, 2], msgs)
395
- ensure
396
- coproc&.stop
397
- end
398
-
399
- def test_that_multiple_messages_sent_at_once_arrive_in_order
400
- msgs = []
401
- coproc = spin { loop { msgs << receive } }
402
-
403
- snooze # allow coproc to start
404
-
405
- 3.times { |i| coproc << i }
406
-
407
- snooze
408
-
409
- assert_equal([0, 1, 2], msgs)
410
- ensure
411
- coproc&.stop
412
- end
413
-
414
- def test_that_sent_message_are_queued_before_calling_receive
415
- buffer = []
416
- receiver = spin { suspend; 3.times { buffer << receive } }
417
- sender = spin { 3.times { |i| receiver << (i * 10) } }
418
-
419
- sender.await
420
- receiver.schedule
421
- receiver.await
422
-
423
- assert_equal [0, 10, 20], buffer
424
- end
425
-
426
- def test_map_and_count
427
- assert_equal 1, Polyphony::Coprocess.count
428
- map = { Fiber.current => Polyphony::Coprocess.current }
429
- assert_equal map, Polyphony::Coprocess.map
430
-
431
- cp = spin { sleep 1 }
432
- snooze
433
- assert_equal 2, Polyphony::Coprocess.count
434
- assert_equal cp, Polyphony::Coprocess.map[cp.fiber]
435
-
436
- cp.stop
437
- snooze
438
- assert_equal 1, Polyphony::Coprocess.count
439
- end
440
- end