polyphony 0.36 → 0.42

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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +28 -2
  6. data/Gemfile +0 -11
  7. data/Gemfile.lock +15 -14
  8. data/README.md +2 -1
  9. data/Rakefile +7 -3
  10. data/TODO.md +28 -95
  11. data/docs/_config.yml +56 -7
  12. data/docs/_sass/custom/custom.scss +0 -30
  13. data/docs/_sass/overrides.scss +0 -46
  14. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  15. data/docs/_user-guide/index.md +9 -0
  16. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  17. data/docs/api-reference/fiber.md +2 -2
  18. data/docs/api-reference/index.md +9 -0
  19. data/docs/api-reference/polyphony-process.md +1 -1
  20. data/docs/api-reference/thread.md +1 -1
  21. data/docs/faq.md +21 -11
  22. data/docs/getting-started/index.md +10 -0
  23. data/docs/getting-started/installing.md +2 -6
  24. data/docs/getting-started/overview.md +507 -0
  25. data/docs/getting-started/tutorial.md +27 -19
  26. data/docs/index.md +3 -2
  27. data/docs/main-concepts/concurrency.md +0 -5
  28. data/docs/main-concepts/design-principles.md +69 -21
  29. data/docs/main-concepts/extending.md +1 -1
  30. data/docs/main-concepts/index.md +9 -0
  31. data/examples/core/01-spinning-up-fibers.rb +1 -0
  32. data/examples/core/03-interrupting.rb +4 -1
  33. data/examples/core/04-handling-signals.rb +19 -0
  34. data/examples/core/xx-agent.rb +102 -0
  35. data/examples/core/xx-fork-cleanup.rb +22 -0
  36. data/examples/core/xx-sleeping.rb +14 -6
  37. data/examples/io/tunnel.rb +48 -0
  38. data/examples/io/xx-irb.rb +1 -1
  39. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  40. data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
  41. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  42. data/examples/performance/xx-array.rb +11 -0
  43. data/examples/performance/xx-fiber-switch.rb +9 -0
  44. data/examples/performance/xx-snooze.rb +15 -0
  45. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  46. data/ext/{gyro → polyphony}/fiber.c +18 -22
  47. data/ext/{gyro → polyphony}/libev.c +0 -0
  48. data/ext/{gyro → polyphony}/libev.h +0 -0
  49. data/ext/polyphony/libev_agent.c +718 -0
  50. data/ext/polyphony/libev_queue.c +216 -0
  51. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -46
  52. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +25 -39
  53. data/ext/polyphony/polyphony_ext.c +23 -0
  54. data/ext/{gyro → polyphony}/socket.c +21 -18
  55. data/ext/polyphony/thread.c +206 -0
  56. data/ext/{gyro → polyphony}/tracing.c +1 -1
  57. data/lib/polyphony.rb +40 -44
  58. data/lib/polyphony/adapters/fs.rb +1 -4
  59. data/lib/polyphony/adapters/irb.rb +1 -1
  60. data/lib/polyphony/adapters/postgres.rb +6 -5
  61. data/lib/polyphony/adapters/process.rb +27 -23
  62. data/lib/polyphony/adapters/trace.rb +110 -105
  63. data/lib/polyphony/core/channel.rb +35 -35
  64. data/lib/polyphony/core/exceptions.rb +29 -29
  65. data/lib/polyphony/core/global_api.rb +94 -91
  66. data/lib/polyphony/core/resource_pool.rb +83 -83
  67. data/lib/polyphony/core/sync.rb +16 -16
  68. data/lib/polyphony/core/thread_pool.rb +49 -37
  69. data/lib/polyphony/core/throttler.rb +30 -23
  70. data/lib/polyphony/event.rb +27 -0
  71. data/lib/polyphony/extensions/core.rb +25 -17
  72. data/lib/polyphony/extensions/fiber.rb +269 -267
  73. data/lib/polyphony/extensions/io.rb +56 -26
  74. data/lib/polyphony/extensions/openssl.rb +5 -9
  75. data/lib/polyphony/extensions/socket.rb +29 -10
  76. data/lib/polyphony/extensions/thread.rb +19 -12
  77. data/lib/polyphony/net.rb +64 -60
  78. data/lib/polyphony/version.rb +1 -1
  79. data/polyphony.gemspec +4 -7
  80. data/test/helper.rb +14 -1
  81. data/test/stress.rb +17 -12
  82. data/test/test_agent.rb +124 -0
  83. data/test/{test_async.rb → test_event.rb} +15 -7
  84. data/test/test_ext.rb +25 -4
  85. data/test/test_fiber.rb +19 -10
  86. data/test/test_global_api.rb +4 -4
  87. data/test/test_io.rb +46 -24
  88. data/test/test_queue.rb +74 -0
  89. data/test/test_signal.rb +3 -40
  90. data/test/test_socket.rb +33 -0
  91. data/test/test_thread.rb +38 -16
  92. data/test/test_thread_pool.rb +2 -2
  93. data/test/test_throttler.rb +0 -1
  94. data/test/test_trace.rb +6 -5
  95. metadata +41 -57
  96. data/docs/_includes/nav.html +0 -51
  97. data/docs/_includes/prevnext.html +0 -17
  98. data/docs/_layouts/default.html +0 -106
  99. data/docs/api-reference.md +0 -11
  100. data/docs/api-reference/gyro-async.md +0 -57
  101. data/docs/api-reference/gyro-child.md +0 -29
  102. data/docs/api-reference/gyro-queue.md +0 -44
  103. data/docs/api-reference/gyro-timer.md +0 -51
  104. data/docs/api-reference/gyro.md +0 -25
  105. data/docs/getting-started.md +0 -10
  106. data/docs/main-concepts.md +0 -10
  107. data/docs/user-guide.md +0 -10
  108. data/examples/core/forever_sleep.rb +0 -19
  109. data/ext/gyro/async.c +0 -148
  110. data/ext/gyro/child.c +0 -127
  111. data/ext/gyro/gyro_ext.c +0 -33
  112. data/ext/gyro/io.c +0 -474
  113. data/ext/gyro/queue.c +0 -142
  114. data/ext/gyro/selector.c +0 -205
  115. data/ext/gyro/signal.c +0 -118
  116. data/ext/gyro/thread.c +0 -298
  117. data/ext/gyro/timer.c +0 -134
  118. data/test/test_timer.rb +0 -56
@@ -2,340 +2,342 @@
2
2
 
3
3
  require 'fiber'
4
4
 
5
- Exceptions = import '../core/exceptions'
6
-
7
- # Fiber control API
8
- module FiberControl
9
- def await
10
- if @running == false
11
- return @result.is_a?(Exception) ? (Kernel.raise @result) : @result
5
+ require_relative '../core/exceptions'
6
+
7
+ module Polyphony
8
+ # Fiber control API
9
+ module FiberControl
10
+ def await
11
+ if @running == false
12
+ return @result.is_a?(Exception) ? (Kernel.raise @result) : @result
13
+ end
14
+
15
+ fiber = Fiber.current
16
+ @waiting_fibers ||= {}
17
+ @waiting_fibers[fiber] = true
18
+ suspend
19
+ ensure
20
+ @waiting_fibers&.delete(fiber)
12
21
  end
22
+ alias_method :join, :await
13
23
 
14
- fiber = Fiber.current
15
- @waiting_fibers ||= {}
16
- @waiting_fibers[fiber] = true
17
- suspend
18
- ensure
19
- @waiting_fibers&.delete(fiber)
20
- end
21
- alias_method :join, :await
24
+ def interrupt(value = nil)
25
+ return if @running == false
22
26
 
23
- def interrupt(value = nil)
24
- return if @running == false
27
+ schedule Polyphony::MoveOn.new(value)
28
+ end
29
+ alias_method :stop, :interrupt
25
30
 
26
- schedule Exceptions::MoveOn.new(value)
27
- end
28
- alias_method :stop, :interrupt
31
+ def restart(value = nil)
32
+ raise "Can''t restart main fiber" if @main
29
33
 
30
- def restart(value = nil)
31
- raise "Can''t restart main fiber" if @main
34
+ if @running
35
+ schedule Polyphony::Restart.new(value)
36
+ return self
37
+ end
32
38
 
33
- if @running
34
- schedule Exceptions::Restart.new(value)
35
- return self
39
+ parent.spin(@tag, @caller, &@block).tap do |f|
40
+ f.schedule(value) unless value.nil?
41
+ end
36
42
  end
43
+ alias_method :reset, :restart
37
44
 
38
- parent.spin(@tag, @caller, &@block).tap do |f|
39
- f.schedule(value) unless value.nil?
40
- end
41
- end
42
- alias_method :reset, :restart
43
-
44
- def cancel
45
- return if @running == false
46
-
47
- schedule Exceptions::Cancel.new
48
- end
45
+ def cancel
46
+ return if @running == false
49
47
 
50
- def terminate
51
- return if @running == false
48
+ schedule Polyphony::Cancel.new
49
+ end
52
50
 
53
- schedule Exceptions::Terminate.new
54
- end
51
+ def terminate
52
+ return if @running == false
55
53
 
56
- def raise(*args)
57
- error = error_from_raise_args(args)
58
- schedule(error)
59
- end
54
+ schedule Polyphony::Terminate.new
55
+ end
60
56
 
61
- def error_from_raise_args(args)
62
- case (arg = args.shift)
63
- when String then RuntimeError.new(arg)
64
- when Class then arg.new(args.shift)
65
- when Exception then arg
66
- else RuntimeError.new
57
+ def raise(*args)
58
+ error = error_from_raise_args(args)
59
+ schedule(error)
67
60
  end
68
- end
69
- end
70
61
 
71
- # Fiber supervision
72
- module FiberSupervision
73
- def supervise(opts = {})
74
- @counter = 0
75
- @on_child_done = proc do |fiber, result|
76
- self << fiber unless result.is_a?(Exception)
62
+ def error_from_raise_args(args)
63
+ case (arg = args.shift)
64
+ when String then RuntimeError.new(arg)
65
+ when Class then arg.new(args.shift)
66
+ when Exception then arg
67
+ else RuntimeError.new
68
+ end
77
69
  end
78
- loop { supervise_perform(opts) }
79
- ensure
80
- @on_child_done = nil
81
70
  end
82
71
 
83
- def supervise_perform(opts)
84
- fiber = receive
85
- restart_fiber(fiber, opts) if fiber
86
- rescue Exceptions::Restart
87
- restart_all_children
88
- rescue Exception => e
89
- Kernel.raise e if e.source_fiber.nil? || e.source_fiber == self
72
+ # Fiber supervision
73
+ module FiberSupervision
74
+ def supervise(opts = {})
75
+ @counter = 0
76
+ @on_child_done = proc do |fiber, result|
77
+ self << fiber unless result.is_a?(Exception)
78
+ end
79
+ loop { supervise_perform(opts) }
80
+ ensure
81
+ @on_child_done = nil
82
+ end
90
83
 
91
- restart_fiber(e.source_fiber, opts)
92
- end
84
+ def supervise_perform(opts)
85
+ fiber = receive
86
+ restart_fiber(fiber, opts) if fiber
87
+ rescue Polyphony::Restart
88
+ restart_all_children
89
+ rescue Exception => e
90
+ Kernel.raise e if e.source_fiber.nil? || e.source_fiber == self
93
91
 
94
- def restart_fiber(fiber, opts)
95
- opts[:watcher]&.send [:restart, fiber]
96
- case opts[:restart]
97
- when true
98
- fiber.restart
99
- when :one_for_all
100
- @children.keys.each(&:restart)
92
+ restart_fiber(e.source_fiber, opts)
101
93
  end
102
- end
103
- end
104
94
 
105
- # Class methods for controlling fibers (namely await and select)
106
- module FiberControlClassMethods
107
- def await(*fibers)
108
- return [] if fibers.empty?
109
-
110
- state = setup_await_select_state(fibers)
111
- await_setup_monitoring(fibers, state)
112
- suspend
113
- fibers.map(&:result)
114
- ensure
115
- await_select_cleanup(state)
95
+ def restart_fiber(fiber, opts)
96
+ opts[:watcher]&.send [:restart, fiber]
97
+ case opts[:restart]
98
+ when true
99
+ fiber.restart
100
+ when :one_for_all
101
+ @children.keys.each(&:restart)
102
+ end
103
+ end
116
104
  end
117
- alias_method :join, :await
118
105
 
119
- def setup_await_select_state(fibers)
120
- {
121
- awaiter: Fiber.current,
122
- pending: fibers.each_with_object({}) { |f, h| h[f] = true }
123
- }
124
- end
106
+ # Class methods for controlling fibers (namely await and select)
107
+ module FiberControlClassMethods
108
+ def await(*fibers)
109
+ return [] if fibers.empty?
125
110
 
126
- def await_setup_monitoring(fibers, state)
127
- fibers.each do |f|
128
- f.when_done { |r| await_fiber_done(f, r, state) }
111
+ state = setup_await_select_state(fibers)
112
+ await_setup_monitoring(fibers, state)
113
+ suspend
114
+ fibers.map(&:result)
115
+ ensure
116
+ await_select_cleanup(state)
129
117
  end
130
- end
118
+ alias_method :join, :await
131
119
 
132
- def await_fiber_done(fiber, result, state)
133
- state[:pending].delete(fiber)
120
+ def setup_await_select_state(fibers)
121
+ {
122
+ awaiter: Fiber.current,
123
+ pending: fibers.each_with_object({}) { |f, h| h[f] = true }
124
+ }
125
+ end
134
126
 
135
- if state[:cleanup]
136
- state[:awaiter].schedule if state[:pending].empty?
137
- elsif !state[:done] && (result.is_a?(Exception) || state[:pending].empty?)
138
- state[:awaiter].schedule(result)
139
- state[:done] = true
127
+ def await_setup_monitoring(fibers, state)
128
+ fibers.each do |f|
129
+ f.when_done { |r| await_fiber_done(f, r, state) }
130
+ end
140
131
  end
141
- end
142
132
 
143
- def await_select_cleanup(state)
144
- return if state[:pending].empty?
133
+ def await_fiber_done(fiber, result, state)
134
+ state[:pending].delete(fiber)
145
135
 
146
- terminate = Exceptions::Terminate.new
147
- state[:cleanup] = true
148
- state[:pending].each_key { |f| f.schedule(terminate) }
149
- suspend
150
- end
136
+ if state[:cleanup]
137
+ state[:awaiter].schedule if state[:pending].empty?
138
+ elsif !state[:done] && (result.is_a?(Exception) || state[:pending].empty?)
139
+ state[:awaiter].schedule(result)
140
+ state[:done] = true
141
+ end
142
+ end
151
143
 
152
- def select(*fibers)
153
- state = setup_await_select_state(fibers)
154
- select_setup_monitoring(fibers, state)
155
- suspend
156
- ensure
157
- await_select_cleanup(state)
158
- end
144
+ def await_select_cleanup(state)
145
+ return if state[:pending].empty?
159
146
 
160
- def select_setup_monitoring(fibers, state)
161
- fibers.each do |f|
162
- f.when_done { |r| select_fiber_done(f, r, state) }
147
+ terminate = Polyphony::Terminate.new
148
+ state[:cleanup] = true
149
+ state[:pending].each_key { |f| f.schedule(terminate) }
150
+ suspend
163
151
  end
164
- end
165
152
 
166
- def select_fiber_done(fiber, result, state)
167
- state[:pending].delete(fiber)
168
- if state[:cleanup]
169
- # in cleanup mode the selector is resumed if no more pending fibers
170
- state[:awaiter].schedule if state[:pending].empty?
171
- elsif !state[:selected]
172
- # first fiber to complete, we schedule the result
173
- state[:awaiter].schedule([fiber, result])
174
- state[:selected] = true
153
+ def select(*fibers)
154
+ state = setup_await_select_state(fibers)
155
+ select_setup_monitoring(fibers, state)
156
+ suspend
157
+ ensure
158
+ await_select_cleanup(state)
175
159
  end
176
- end
177
- end
178
160
 
179
- # Messaging functionality
180
- module FiberMessaging
181
- def <<(value)
182
- @mailbox << value
183
- end
184
- alias_method :send, :<<
161
+ def select_setup_monitoring(fibers, state)
162
+ fibers.each do |f|
163
+ f.when_done { |r| select_fiber_done(f, r, state) }
164
+ end
165
+ end
185
166
 
186
- def receive
187
- @mailbox.shift
167
+ def select_fiber_done(fiber, result, state)
168
+ state[:pending].delete(fiber)
169
+ if state[:cleanup]
170
+ # in cleanup mode the selector is resumed if no more pending fibers
171
+ state[:awaiter].schedule if state[:pending].empty?
172
+ elsif !state[:selected]
173
+ # first fiber to complete, we schedule the result
174
+ state[:awaiter].schedule([fiber, result])
175
+ state[:selected] = true
176
+ end
177
+ end
188
178
  end
189
179
 
190
- def receive_pending
191
- @mailbox.shift_each
192
- end
193
- end
180
+ # Messaging functionality
181
+ module FiberMessaging
182
+ def <<(value)
183
+ @mailbox << value
184
+ end
185
+ alias_method :send, :<<
194
186
 
195
- # Methods for controlling child fibers
196
- module ChildFiberControl
197
- def children
198
- (@children ||= {}).keys
199
- end
187
+ def receive
188
+ @mailbox.shift
189
+ end
200
190
 
201
- def spin(tag = nil, orig_caller = Kernel.caller, &block)
202
- f = Fiber.new { |v| f.run(v) }
203
- f.prepare(tag, block, orig_caller, self)
204
- (@children ||= {})[f] = true
205
- f
191
+ def receive_pending
192
+ @mailbox.shift_each
193
+ end
206
194
  end
207
195
 
208
- def child_done(child_fiber, result)
209
- @children.delete(child_fiber)
210
- @on_child_done&.(child_fiber, result)
211
- end
196
+ # Methods for controlling child fibers
197
+ module ChildFiberControl
198
+ def children
199
+ (@children ||= {}).keys
200
+ end
212
201
 
213
- def terminate_all_children
214
- return unless @children
202
+ def spin(tag = nil, orig_caller = Kernel.caller, &block)
203
+ f = Fiber.new { |v| f.run(v) }
204
+ f.prepare(tag, block, orig_caller, self)
205
+ (@children ||= {})[f] = true
206
+ f
207
+ end
215
208
 
216
- e = Exceptions::Terminate.new
217
- @children.each_key { |c| c.raise e }
218
- end
209
+ def child_done(child_fiber, result)
210
+ @children.delete(child_fiber)
211
+ @on_child_done&.(child_fiber, result)
212
+ end
219
213
 
220
- def await_all_children
221
- return unless @children && !@children.empty?
214
+ def terminate_all_children
215
+ return unless @children
222
216
 
223
- Fiber.await(*@children.keys)
224
- end
217
+ e = Polyphony::Terminate.new
218
+ @children.each_key { |c| c.raise e }
219
+ end
225
220
 
226
- def shutdown_all_children
227
- terminate_all_children
228
- await_all_children
229
- end
230
- end
221
+ def await_all_children
222
+ return unless @children && !@children.empty?
231
223
 
232
- # Fiber life cycle methods
233
- module FiberLifeCycle
234
- def prepare(tag, block, caller, parent)
235
- @thread = Thread.current
236
- @tag = tag
237
- @parent = parent
238
- @caller = caller
239
- @block = block
240
- @mailbox = Gyro::Queue.new
241
- __fiber_trace__(:fiber_create, self)
242
- schedule
243
- end
224
+ Fiber.await(*@children.keys)
225
+ end
244
226
 
245
- def run(first_value)
246
- setup first_value
247
- result = @block.(first_value)
248
- finalize result
249
- rescue Exceptions::Restart => e
250
- restart_self(e.value)
251
- rescue Exceptions::MoveOn, Exceptions::Terminate => e
252
- finalize e.value
253
- rescue Exception => e
254
- e.source_fiber = self
255
- finalize e, true
227
+ def shutdown_all_children
228
+ terminate_all_children
229
+ await_all_children
230
+ end
256
231
  end
257
232
 
258
- def setup(first_value)
259
- Kernel.raise first_value if first_value.is_a?(Exception)
233
+ # Fiber life cycle methods
234
+ module FiberLifeCycle
235
+ def prepare(tag, block, caller, parent)
236
+ @thread = Thread.current
237
+ @tag = tag
238
+ @parent = parent
239
+ @caller = caller
240
+ @block = block
241
+ @mailbox = Polyphony::Queue.new
242
+ __fiber_trace__(:fiber_create, self)
243
+ schedule
244
+ end
260
245
 
261
- @running = true
262
- end
246
+ def run(first_value)
247
+ setup first_value
248
+ result = @block.(first_value)
249
+ finalize result
250
+ rescue Polyphony::Restart => e
251
+ restart_self(e.value)
252
+ rescue Polyphony::MoveOn, Polyphony::Terminate => e
253
+ finalize e.value
254
+ rescue Exception => e
255
+ e.source_fiber = self
256
+ finalize e, true
257
+ end
263
258
 
264
- # Performs setup for a "raw" Fiber created using Fiber.new. Note that this
265
- # fiber is an orphan fiber (has no parent), since we cannot control how the
266
- # fiber terminates after it has already been created. Calling #setup_raw
267
- # allows the fiber to be scheduled and to receive messages.
268
- def setup_raw
269
- @thread = Thread.current
270
- @mailbox = Gyro::Queue.new
271
- end
259
+ def setup(first_value)
260
+ Kernel.raise first_value if first_value.is_a?(Exception)
272
261
 
273
- def setup_main_fiber
274
- @main = true
275
- @tag = :main
276
- @thread = Thread.current
277
- @running = true
278
- @children&.clear
279
- @mailbox = Gyro::Queue.new
280
- end
262
+ @running = true
263
+ end
281
264
 
282
- def restart_self(first_value)
283
- @mailbox = Gyro::Queue.new
284
- @when_done_procs = nil
285
- @waiting_fibers = nil
286
- run(first_value)
287
- end
265
+ # Performs setup for a "raw" Fiber created using Fiber.new. Note that this
266
+ # fiber is an orphan fiber (has no parent), since we cannot control how the
267
+ # fiber terminates after it has already been created. Calling #setup_raw
268
+ # allows the fiber to be scheduled and to receive messages.
269
+ def setup_raw
270
+ @thread = Thread.current
271
+ @mailbox = Polyphony::Queue.new
272
+ end
288
273
 
289
- def finalize(result, uncaught_exception = false)
290
- result, uncaught_exception = finalize_children(result, uncaught_exception)
291
- __fiber_trace__(:fiber_terminate, self, result)
292
- @result = result
293
- @running = false
294
- inform_dependants(result, uncaught_exception)
295
- ensure
296
- Thread.current.switch_fiber
297
- end
274
+ def setup_main_fiber
275
+ @main = true
276
+ @tag = :main
277
+ @thread = Thread.current
278
+ @running = true
279
+ @children&.clear
280
+ @mailbox = Polyphony::Queue.new
281
+ end
298
282
 
299
- # Shuts down all children of the current fiber. If any exception occurs while
300
- # the children are shut down, it is returned along with the uncaught_exception
301
- # flag set. Otherwise, it returns the given arguments.
302
- def finalize_children(result, uncaught_exception)
303
- begin
304
- shutdown_all_children
305
- rescue Exception => e
306
- result = e
307
- uncaught_exception = true
283
+ def restart_self(first_value)
284
+ @mailbox = Polyphony::Queue.new
285
+ @when_done_procs = nil
286
+ @waiting_fibers = nil
287
+ run(first_value)
308
288
  end
309
- [result, uncaught_exception]
310
- end
311
289
 
312
- def inform_dependants(result, uncaught_exception)
313
- @parent&.child_done(self, result)
314
- @when_done_procs&.each { |p| p.(result) }
315
- @waiting_fibers&.each_key do |f|
316
- f.schedule(result)
290
+ def finalize(result, uncaught_exception = false)
291
+ result, uncaught_exception = finalize_children(result, uncaught_exception)
292
+ __fiber_trace__(:fiber_terminate, self, result)
293
+ @result = result
294
+ @running = false
295
+ inform_dependants(result, uncaught_exception)
296
+ ensure
297
+ Thread.current.switch_fiber
317
298
  end
318
- return unless uncaught_exception && !@waiting_fibers
319
299
 
320
- # propagate unaught exception to parent
321
- @parent&.schedule(result)
322
- end
300
+ # Shuts down all children of the current fiber. If any exception occurs while
301
+ # the children are shut down, it is returned along with the uncaught_exception
302
+ # flag set. Otherwise, it returns the given arguments.
303
+ def finalize_children(result, uncaught_exception)
304
+ begin
305
+ shutdown_all_children
306
+ rescue Exception => e
307
+ result = e
308
+ uncaught_exception = true
309
+ end
310
+ [result, uncaught_exception]
311
+ end
323
312
 
324
- def when_done(&block)
325
- @when_done_procs ||= []
326
- @when_done_procs << block
313
+ def inform_dependants(result, uncaught_exception)
314
+ @parent&.child_done(self, result)
315
+ @when_done_procs&.each { |p| p.(result) }
316
+ @waiting_fibers&.each_key do |f|
317
+ f.schedule(result)
318
+ end
319
+ return unless uncaught_exception && !@waiting_fibers
320
+
321
+ # propagate unaught exception to parent
322
+ @parent&.schedule(result)
323
+ end
324
+
325
+ def when_done(&block)
326
+ @when_done_procs ||= []
327
+ @when_done_procs << block
328
+ end
327
329
  end
328
330
  end
329
331
 
330
332
  # Fiber extensions
331
333
  class ::Fiber
332
- prepend FiberControl
333
- include FiberSupervision
334
- include FiberMessaging
335
- include ChildFiberControl
336
- include FiberLifeCycle
334
+ prepend Polyphony::FiberControl
335
+ include Polyphony::FiberSupervision
336
+ include Polyphony::FiberMessaging
337
+ include Polyphony::ChildFiberControl
338
+ include Polyphony::FiberLifeCycle
337
339
 
338
- extend FiberControlClassMethods
340
+ extend Polyphony::FiberControlClassMethods
339
341
 
340
342
  attr_accessor :tag, :thread, :parent
341
343
  attr_reader :result
@@ -377,6 +379,6 @@ orig_pid = Process.pid
377
379
  at_exit do
378
380
  next unless orig_pid == Process.pid
379
381
 
380
- Fiber.current.terminate_all_children
381
- Fiber.current.await_all_children
382
+ Polyphony.terminate_threads
383
+ Fiber.current.shutdown_all_children
382
384
  end