polyphony 0.24 → 0.25

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