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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +28 -2
- data/Gemfile +0 -11
- data/Gemfile.lock +15 -14
- data/README.md +2 -1
- data/Rakefile +7 -3
- data/TODO.md +28 -95
- data/docs/_config.yml +56 -7
- data/docs/_sass/custom/custom.scss +0 -30
- data/docs/_sass/overrides.scss +0 -46
- data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
- data/docs/_user-guide/index.md +9 -0
- data/docs/{user-guide → _user-guide}/web-server.md +0 -0
- data/docs/api-reference/fiber.md +2 -2
- data/docs/api-reference/index.md +9 -0
- data/docs/api-reference/polyphony-process.md +1 -1
- data/docs/api-reference/thread.md +1 -1
- data/docs/faq.md +21 -11
- data/docs/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +2 -6
- data/docs/getting-started/overview.md +507 -0
- data/docs/getting-started/tutorial.md +27 -19
- data/docs/index.md +3 -2
- data/docs/main-concepts/concurrency.md +0 -5
- data/docs/main-concepts/design-principles.md +69 -21
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/index.md +9 -0
- data/examples/core/01-spinning-up-fibers.rb +1 -0
- data/examples/core/03-interrupting.rb +4 -1
- data/examples/core/04-handling-signals.rb +19 -0
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-fork-cleanup.rb +22 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/io/tunnel.rb +48 -0
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
- data/examples/performance/xx-array.rb +11 -0
- data/examples/performance/xx-fiber-switch.rb +9 -0
- data/examples/performance/xx-snooze.rb +15 -0
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/{gyro → polyphony}/fiber.c +18 -22
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +718 -0
- data/ext/polyphony/libev_queue.c +216 -0
- data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -46
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +25 -39
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +21 -18
- data/ext/polyphony/thread.c +206 -0
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +40 -44
- data/lib/polyphony/adapters/fs.rb +1 -4
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +27 -23
- data/lib/polyphony/adapters/trace.rb +110 -105
- data/lib/polyphony/core/channel.rb +35 -35
- data/lib/polyphony/core/exceptions.rb +29 -29
- data/lib/polyphony/core/global_api.rb +94 -91
- data/lib/polyphony/core/resource_pool.rb +83 -83
- data/lib/polyphony/core/sync.rb +16 -16
- data/lib/polyphony/core/thread_pool.rb +49 -37
- data/lib/polyphony/core/throttler.rb +30 -23
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +25 -17
- data/lib/polyphony/extensions/fiber.rb +269 -267
- data/lib/polyphony/extensions/io.rb +56 -26
- data/lib/polyphony/extensions/openssl.rb +5 -9
- data/lib/polyphony/extensions/socket.rb +29 -10
- data/lib/polyphony/extensions/thread.rb +19 -12
- data/lib/polyphony/net.rb +64 -60
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +4 -7
- data/test/helper.rb +14 -1
- data/test/stress.rb +17 -12
- data/test/test_agent.rb +124 -0
- data/test/{test_async.rb → test_event.rb} +15 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +4 -4
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +33 -0
- data/test/test_thread.rb +38 -16
- data/test/test_thread_pool.rb +2 -2
- data/test/test_throttler.rb +0 -1
- data/test/test_trace.rb +6 -5
- metadata +41 -57
- data/docs/_includes/nav.html +0 -51
- data/docs/_includes/prevnext.html +0 -17
- data/docs/_layouts/default.html +0 -106
- data/docs/api-reference.md +0 -11
- data/docs/api-reference/gyro-async.md +0 -57
- data/docs/api-reference/gyro-child.md +0 -29
- data/docs/api-reference/gyro-queue.md +0 -44
- data/docs/api-reference/gyro-timer.md +0 -51
- data/docs/api-reference/gyro.md +0 -25
- data/docs/getting-started.md +0 -10
- data/docs/main-concepts.md +0 -10
- data/docs/user-guide.md +0 -10
- data/examples/core/forever_sleep.rb +0 -19
- data/ext/gyro/async.c +0 -148
- data/ext/gyro/child.c +0 -127
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -474
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -205
- data/ext/gyro/signal.c +0 -118
- data/ext/gyro/thread.c +0 -298
- data/ext/gyro/timer.c +0 -134
- data/test/test_timer.rb +0 -56
@@ -2,340 +2,342 @@
|
|
2
2
|
|
3
3
|
require 'fiber'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
15
|
-
|
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
|
-
|
24
|
-
|
27
|
+
schedule Polyphony::MoveOn.new(value)
|
28
|
+
end
|
29
|
+
alias_method :stop, :interrupt
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
alias_method :stop, :interrupt
|
31
|
+
def restart(value = nil)
|
32
|
+
raise "Can''t restart main fiber" if @main
|
29
33
|
|
30
|
-
|
31
|
-
|
34
|
+
if @running
|
35
|
+
schedule Polyphony::Restart.new(value)
|
36
|
+
return self
|
37
|
+
end
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
51
|
-
|
48
|
+
schedule Polyphony::Cancel.new
|
49
|
+
end
|
52
50
|
|
53
|
-
|
54
|
-
|
51
|
+
def terminate
|
52
|
+
return if @running == false
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
schedule(error)
|
59
|
-
end
|
54
|
+
schedule Polyphony::Terminate.new
|
55
|
+
end
|
60
56
|
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
92
|
-
|
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
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
118
|
+
alias_method :join, :await
|
131
119
|
|
132
|
-
|
133
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
144
|
-
|
133
|
+
def await_fiber_done(fiber, result, state)
|
134
|
+
state[:pending].delete(fiber)
|
145
135
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
153
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
187
|
-
|
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
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
180
|
+
# Messaging functionality
|
181
|
+
module FiberMessaging
|
182
|
+
def <<(value)
|
183
|
+
@mailbox << value
|
184
|
+
end
|
185
|
+
alias_method :send, :<<
|
194
186
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
(@children ||= {}).keys
|
199
|
-
end
|
187
|
+
def receive
|
188
|
+
@mailbox.shift
|
189
|
+
end
|
200
190
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
(@children ||= {})[f] = true
|
205
|
-
f
|
191
|
+
def receive_pending
|
192
|
+
@mailbox.shift_each
|
193
|
+
end
|
206
194
|
end
|
207
195
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
196
|
+
# Methods for controlling child fibers
|
197
|
+
module ChildFiberControl
|
198
|
+
def children
|
199
|
+
(@children ||= {}).keys
|
200
|
+
end
|
212
201
|
|
213
|
-
|
214
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
209
|
+
def child_done(child_fiber, result)
|
210
|
+
@children.delete(child_fiber)
|
211
|
+
@on_child_done&.(child_fiber, result)
|
212
|
+
end
|
219
213
|
|
220
|
-
|
221
|
-
|
214
|
+
def terminate_all_children
|
215
|
+
return unless @children
|
222
216
|
|
223
|
-
|
224
|
-
|
217
|
+
e = Polyphony::Terminate.new
|
218
|
+
@children.each_key { |c| c.raise e }
|
219
|
+
end
|
225
220
|
|
226
|
-
|
227
|
-
|
228
|
-
await_all_children
|
229
|
-
end
|
230
|
-
end
|
221
|
+
def await_all_children
|
222
|
+
return unless @children && !@children.empty?
|
231
223
|
|
232
|
-
|
233
|
-
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
259
|
-
|
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
|
-
|
262
|
-
|
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
|
-
|
265
|
-
|
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
|
-
|
274
|
-
|
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
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
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
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
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
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
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
|
-
#
|
321
|
-
|
322
|
-
|
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
|
-
|
325
|
-
|
326
|
-
|
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
|
-
|
381
|
-
Fiber.current.
|
382
|
+
Polyphony.terminate_threads
|
383
|
+
Fiber.current.shutdown_all_children
|
382
384
|
end
|