polyphony 0.57.0 → 0.60
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/CHANGELOG.md +27 -0
- data/Gemfile.lock +15 -29
- data/examples/core/message_based_supervision.rb +51 -0
- data/ext/polyphony/backend_common.c +108 -3
- data/ext/polyphony/backend_common.h +23 -0
- data/ext/polyphony/backend_io_uring.c +117 -39
- data/ext/polyphony/backend_io_uring_context.c +11 -3
- data/ext/polyphony/backend_io_uring_context.h +5 -3
- data/ext/polyphony/backend_libev.c +92 -30
- data/ext/polyphony/extconf.rb +2 -2
- data/ext/polyphony/fiber.c +1 -34
- data/ext/polyphony/polyphony.c +12 -19
- data/ext/polyphony/polyphony.h +10 -20
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/queue.c +12 -12
- data/ext/polyphony/runqueue.c +17 -85
- data/ext/polyphony/runqueue.h +27 -0
- data/ext/polyphony/thread.c +10 -99
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/extensions/fiber.rb +102 -82
- data/lib/polyphony/extensions/io.rb +10 -9
- data/lib/polyphony/extensions/openssl.rb +14 -4
- data/lib/polyphony/extensions/socket.rb +15 -15
- data/lib/polyphony/extensions/thread.rb +8 -0
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +0 -7
- data/test/test_backend.rb +71 -5
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +106 -18
- data/test/test_global_api.rb +1 -1
- data/test/test_io.rb +29 -0
- data/test/test_supervise.rb +100 -100
- data/test/test_thread.rb +57 -11
- data/test/test_thread_pool.rb +1 -1
- data/test/test_trace.rb +28 -49
- metadata +4 -108
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
data/lib/polyphony/core/timer.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Polyphony
|
4
4
|
# Implements a common timer for running multiple timeouts
|
5
5
|
class Timer
|
6
|
-
def initialize(resolution:)
|
7
|
-
@fiber = spin_loop(interval: resolution) { update }
|
6
|
+
def initialize(tag = nil, resolution:)
|
7
|
+
@fiber = spin_loop(tag, interval: resolution) { update }
|
8
8
|
@timeouts = {}
|
9
9
|
end
|
10
10
|
|
@@ -22,9 +22,9 @@ module Polyphony
|
|
22
22
|
return self
|
23
23
|
end
|
24
24
|
|
25
|
-
parent.spin(@tag, @caller, &@block)
|
26
|
-
|
27
|
-
|
25
|
+
fiber = parent.spin(@tag, @caller, &@block)
|
26
|
+
fiber.schedule(value) unless value.nil?
|
27
|
+
fiber
|
28
28
|
end
|
29
29
|
alias_method :reset, :restart
|
30
30
|
|
@@ -66,6 +66,11 @@ module Polyphony
|
|
66
66
|
def interject(&block)
|
67
67
|
raise Polyphony::Interjection.new(block)
|
68
68
|
end
|
69
|
+
|
70
|
+
def await
|
71
|
+
Fiber.await(self).first
|
72
|
+
end
|
73
|
+
alias_method :join, :await
|
69
74
|
end
|
70
75
|
|
71
76
|
# Fiber supervision
|
@@ -78,7 +83,7 @@ module Polyphony
|
|
78
83
|
while true
|
79
84
|
supervise_perform(opts)
|
80
85
|
end
|
81
|
-
|
86
|
+
rescue Polyphony::MoveOn
|
82
87
|
# generated in #supervise_perform to stop supervisor
|
83
88
|
ensure
|
84
89
|
@on_child_done = nil
|
@@ -119,72 +124,62 @@ module Polyphony
|
|
119
124
|
def await(*fibers)
|
120
125
|
return [] if fibers.empty?
|
121
126
|
|
122
|
-
|
123
|
-
|
124
|
-
suspend
|
125
|
-
fibers.map(&:result)
|
126
|
-
ensure
|
127
|
-
await_select_cleanup(state) if state
|
128
|
-
end
|
129
|
-
alias_method :join, :await
|
130
|
-
|
131
|
-
def setup_await_select_state(fibers)
|
132
|
-
{
|
133
|
-
awaiter: Fiber.current,
|
134
|
-
pending: fibers.each_with_object({}) { |f, h| h[f] = true }
|
135
|
-
}
|
136
|
-
end
|
137
|
-
|
138
|
-
def await_setup_monitoring(fibers, state)
|
127
|
+
Fiber.current.message_on_child_termination = true
|
128
|
+
results = {}
|
139
129
|
fibers.each do |f|
|
140
|
-
f
|
130
|
+
results[f] = nil
|
131
|
+
if f.dead?
|
132
|
+
# fiber already terminated, so queue message
|
133
|
+
Fiber.current.send [f, f.result]
|
134
|
+
else
|
135
|
+
f.monitor
|
136
|
+
end
|
141
137
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
138
|
+
exception = nil
|
139
|
+
while !fibers.empty?
|
140
|
+
(fiber, result) = receive
|
141
|
+
next unless fibers.include?(fiber)
|
142
|
+
|
143
|
+
fibers.delete(fiber)
|
144
|
+
if result.is_a?(Exception)
|
145
|
+
exception ||= result
|
146
|
+
fibers.each { |f| f.terminate }
|
147
|
+
else
|
148
|
+
results[fiber] = result
|
149
|
+
end
|
152
150
|
end
|
153
|
-
|
154
|
-
|
155
|
-
def await_select_cleanup(state)
|
156
|
-
return if state[:pending].empty?
|
157
|
-
|
158
|
-
terminate = Polyphony::Terminate.new
|
159
|
-
state[:cleanup] = true
|
160
|
-
state[:pending].each_key { |f| f.schedule(terminate) }
|
161
|
-
suspend
|
162
|
-
end
|
163
|
-
|
164
|
-
def select(*fibers)
|
165
|
-
state = setup_await_select_state(fibers)
|
166
|
-
select_setup_monitoring(fibers, state)
|
167
|
-
suspend
|
151
|
+
results.values
|
168
152
|
ensure
|
169
|
-
|
153
|
+
Fiber.current.message_on_child_termination = false
|
154
|
+
raise exception if exception
|
170
155
|
end
|
156
|
+
alias_method :join, :await
|
171
157
|
|
172
|
-
def
|
158
|
+
def select(*fibers)
|
159
|
+
return nil if fibers.empty?
|
160
|
+
|
173
161
|
fibers.each do |f|
|
174
|
-
f.
|
162
|
+
if f.dead?
|
163
|
+
result = f.result
|
164
|
+
result.is_a?(Exception) ? (raise result) : (return [f, result])
|
165
|
+
end
|
175
166
|
end
|
176
|
-
end
|
177
167
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
168
|
+
Fiber.current.message_on_child_termination = true
|
169
|
+
fibers.each { |f| f.monitor }
|
170
|
+
while true
|
171
|
+
(fiber, result) = receive
|
172
|
+
next unless fibers.include?(fiber)
|
173
|
+
|
174
|
+
fibers.each { |f| f.unmonitor }
|
175
|
+
if result.is_a?(Exception)
|
176
|
+
raise result
|
177
|
+
else
|
178
|
+
return [fiber, result]
|
179
|
+
end
|
187
180
|
end
|
181
|
+
ensure
|
182
|
+
Fiber.current.message_on_child_termination = false
|
188
183
|
end
|
189
184
|
|
190
185
|
# Creates and schedules with priority an out-of-band fiber that runs the
|
@@ -209,6 +204,14 @@ module Polyphony
|
|
209
204
|
(@children ||= {}).keys
|
210
205
|
end
|
211
206
|
|
207
|
+
def add_child(child_fiber)
|
208
|
+
(@children ||= {})[child_fiber] = true
|
209
|
+
end
|
210
|
+
|
211
|
+
def remove_child(child_fiber)
|
212
|
+
@children.delete(child_fiber) if @children
|
213
|
+
end
|
214
|
+
|
212
215
|
def spin(tag = nil, orig_caller = Kernel.caller, &block)
|
213
216
|
f = Fiber.new { |v| f.run(v) }
|
214
217
|
f.prepare(tag, block, orig_caller, self)
|
@@ -218,7 +221,10 @@ module Polyphony
|
|
218
221
|
|
219
222
|
def child_done(child_fiber, result)
|
220
223
|
@children.delete(child_fiber)
|
221
|
-
|
224
|
+
|
225
|
+
if result.is_a?(Exception) && !@message_on_child_termination
|
226
|
+
schedule_with_priority(result)
|
227
|
+
end
|
222
228
|
end
|
223
229
|
|
224
230
|
def terminate_all_children(graceful = false)
|
@@ -234,14 +240,7 @@ module Polyphony
|
|
234
240
|
def await_all_children
|
235
241
|
return unless @children && !@children.empty?
|
236
242
|
|
237
|
-
|
238
|
-
@on_child_done = proc do |c, r|
|
239
|
-
results[c] = r
|
240
|
-
schedule if @children.empty?
|
241
|
-
end
|
242
|
-
suspend
|
243
|
-
@on_child_done = nil
|
244
|
-
results.values
|
243
|
+
Fiber.await(*@children.keys)
|
245
244
|
end
|
246
245
|
|
247
246
|
def shutdown_all_children(graceful = false)
|
@@ -252,6 +251,18 @@ module Polyphony
|
|
252
251
|
c.await
|
253
252
|
end
|
254
253
|
end
|
254
|
+
|
255
|
+
def detach
|
256
|
+
@parent.remove_child(self)
|
257
|
+
@parent = @thread.main_fiber
|
258
|
+
@parent.add_child(self)
|
259
|
+
end
|
260
|
+
|
261
|
+
def attach(parent)
|
262
|
+
@parent.remove_child(self)
|
263
|
+
@parent = parent
|
264
|
+
@parent.add_child(self)
|
265
|
+
end
|
255
266
|
end
|
256
267
|
|
257
268
|
# Fiber life cycle methods
|
@@ -262,7 +273,7 @@ module Polyphony
|
|
262
273
|
@parent = parent
|
263
274
|
@caller = caller
|
264
275
|
@block = block
|
265
|
-
|
276
|
+
Thread.backend.trace(:fiber_create, self)
|
266
277
|
schedule
|
267
278
|
end
|
268
279
|
|
@@ -304,17 +315,16 @@ module Polyphony
|
|
304
315
|
|
305
316
|
def restart_self(first_value)
|
306
317
|
@mailbox = nil
|
307
|
-
@when_done_procs = nil
|
308
|
-
@waiting_fibers = nil
|
309
318
|
run(first_value)
|
310
319
|
end
|
311
320
|
|
312
321
|
def finalize(result, uncaught_exception = false)
|
313
322
|
result, uncaught_exception = finalize_children(result, uncaught_exception)
|
314
|
-
|
323
|
+
Thread.backend.trace(:fiber_terminate, self, result)
|
315
324
|
@result = result
|
316
|
-
|
325
|
+
|
317
326
|
inform_dependants(result, uncaught_exception)
|
327
|
+
@running = false
|
318
328
|
ensure
|
319
329
|
# Prevent fiber from being resumed after terminating
|
320
330
|
@thread.fiber_unschedule(self)
|
@@ -332,17 +342,27 @@ module Polyphony
|
|
332
342
|
end
|
333
343
|
|
334
344
|
def inform_dependants(result, uncaught_exception)
|
345
|
+
if @monitors
|
346
|
+
msg = [self, result]
|
347
|
+
@monitors.each { |f| f << msg }
|
348
|
+
end
|
349
|
+
|
335
350
|
@parent&.child_done(self, result)
|
336
|
-
@when_done_procs&.each { |p| p.(result) }
|
337
|
-
@waiting_fibers&.each_key { |f| f.schedule(result) }
|
338
|
-
|
339
|
-
# propagate unaught exception to parent
|
340
|
-
@parent&.schedule_with_priority(result) if uncaught_exception && !@waiting_fibers
|
341
351
|
end
|
342
352
|
|
343
|
-
|
344
|
-
|
345
|
-
|
353
|
+
attr_accessor :message_on_child_termination
|
354
|
+
|
355
|
+
def monitor
|
356
|
+
@monitors ||= []
|
357
|
+
@monitors << Fiber.current
|
358
|
+
end
|
359
|
+
|
360
|
+
def unmonitor
|
361
|
+
@monitors.delete(Fiber.current) if @monitors
|
362
|
+
end
|
363
|
+
|
364
|
+
def dead?
|
365
|
+
state == :dead
|
346
366
|
end
|
347
367
|
end
|
348
368
|
end
|
@@ -101,7 +101,7 @@ class ::IO
|
|
101
101
|
return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
|
102
102
|
|
103
103
|
@read_buffer ||= +''
|
104
|
-
Polyphony.backend_read(self, @read_buffer, 8192, false)
|
104
|
+
Polyphony.backend_read(self, @read_buffer, 8192, false, -1)
|
105
105
|
return @read_buffer.slice!(0) if !@read_buffer.empty?
|
106
106
|
|
107
107
|
nil
|
@@ -110,7 +110,7 @@ class ::IO
|
|
110
110
|
alias_method :orig_read, :read
|
111
111
|
def read(len = nil)
|
112
112
|
@read_buffer ||= +''
|
113
|
-
result = Polyphony.backend_read(self, @read_buffer, len, true)
|
113
|
+
result = Polyphony.backend_read(self, @read_buffer, len, true, -1)
|
114
114
|
return nil unless result
|
115
115
|
|
116
116
|
already_read = @read_buffer
|
@@ -119,8 +119,8 @@ class ::IO
|
|
119
119
|
end
|
120
120
|
|
121
121
|
alias_method :orig_readpartial, :read
|
122
|
-
def readpartial(len, str = +'')
|
123
|
-
result = Polyphony.backend_read(self, str, len, false)
|
122
|
+
def readpartial(len, str = +'', buffer_pos = 0)
|
123
|
+
result = Polyphony.backend_read(self, str, len, false, buffer_pos)
|
124
124
|
raise EOFError unless result
|
125
125
|
|
126
126
|
result
|
@@ -151,9 +151,10 @@ class ::IO
|
|
151
151
|
idx = @read_buffer.index(sep)
|
152
152
|
return @read_buffer.slice!(0, idx + sep_size) if idx
|
153
153
|
|
154
|
-
|
155
|
-
|
156
|
-
@read_buffer
|
154
|
+
result = readpartial(8192, @read_buffer, -1)
|
155
|
+
|
156
|
+
#Polyphony.backend_read(self, @read_buffer, 8192, false, -1)
|
157
|
+
return nil unless result
|
157
158
|
end
|
158
159
|
rescue EOFError
|
159
160
|
return nil
|
@@ -216,8 +217,8 @@ class ::IO
|
|
216
217
|
buf ? readpartial(maxlen, buf) : readpartial(maxlen)
|
217
218
|
end
|
218
219
|
|
219
|
-
def read_loop(&block)
|
220
|
-
Polyphony.backend_read_loop(self, &block)
|
220
|
+
def read_loop(maxlen = 8192, &block)
|
221
|
+
Polyphony.backend_read_loop(self, maxlen, &block)
|
221
222
|
end
|
222
223
|
|
223
224
|
def feed_loop(receiver, method = :call, &block)
|
@@ -64,13 +64,23 @@ class ::OpenSSL::SSL::SSLSocket
|
|
64
64
|
# @sync = osync
|
65
65
|
end
|
66
66
|
|
67
|
-
def readpartial(maxlen, buf = +'')
|
68
|
-
|
67
|
+
def readpartial(maxlen, buf = +'', buffer_pos = 0)
|
68
|
+
if buffer_pos != 0
|
69
|
+
if (result = sysread(maxlen, +''))
|
70
|
+
if buffer_pos == -1
|
71
|
+
result = buf + result
|
72
|
+
else
|
73
|
+
result = buf[0...buffer_pos] + result
|
74
|
+
end
|
75
|
+
end
|
76
|
+
else
|
77
|
+
result = sysread(maxlen, buf)
|
78
|
+
end
|
69
79
|
result || (raise EOFError)
|
70
80
|
end
|
71
81
|
|
72
|
-
def read_loop
|
73
|
-
while (data = sysread(
|
82
|
+
def read_loop(maxlen = 8192)
|
83
|
+
while (data = sysread(maxlen))
|
74
84
|
yield data
|
75
85
|
end
|
76
86
|
end
|
@@ -23,11 +23,11 @@ class ::Socket
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def recv(maxlen, flags = 0, outbuf = nil)
|
26
|
-
Polyphony.backend_recv(self, outbuf || +'', maxlen)
|
26
|
+
Polyphony.backend_recv(self, outbuf || +'', maxlen, 0)
|
27
27
|
end
|
28
28
|
|
29
|
-
def recv_loop(&block)
|
30
|
-
Polyphony.backend_recv_loop(self, &block)
|
29
|
+
def recv_loop(maxlen = 8192, &block)
|
30
|
+
Polyphony.backend_recv_loop(self, maxlen, &block)
|
31
31
|
end
|
32
32
|
alias_method :read_loop, :recv_loop
|
33
33
|
|
@@ -60,8 +60,8 @@ class ::Socket
|
|
60
60
|
# Polyphony.backend_send(self, mesg, 0)
|
61
61
|
# end
|
62
62
|
|
63
|
-
def readpartial(maxlen, str = +'')
|
64
|
-
Polyphony.backend_recv(self, str, maxlen)
|
63
|
+
def readpartial(maxlen, str = +'', buffer_pos = 0)
|
64
|
+
Polyphony.backend_recv(self, str, maxlen, buffer_pos)
|
65
65
|
end
|
66
66
|
|
67
67
|
ZERO_LINGER = [0, 0].pack('ii').freeze
|
@@ -141,11 +141,11 @@ class ::TCPSocket
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def recv(maxlen, flags = 0, outbuf = nil)
|
144
|
-
Polyphony.backend_recv(self, outbuf || +'', maxlen)
|
144
|
+
Polyphony.backend_recv(self, outbuf || +'', maxlen, 0)
|
145
145
|
end
|
146
146
|
|
147
|
-
def recv_loop(&block)
|
148
|
-
Polyphony.backend_recv_loop(self, &block)
|
147
|
+
def recv_loop(maxlen = 8192, &block)
|
148
|
+
Polyphony.backend_recv_loop(self, maxlen, &block)
|
149
149
|
end
|
150
150
|
alias_method :read_loop, :recv_loop
|
151
151
|
|
@@ -165,8 +165,8 @@ class ::TCPSocket
|
|
165
165
|
# Polyphony.backend_send(self, mesg, 0)
|
166
166
|
# end
|
167
167
|
|
168
|
-
def readpartial(maxlen, str = +'')
|
169
|
-
result = Polyphony.backend_recv(self, str, maxlen)
|
168
|
+
def readpartial(maxlen, str = +'', buffer_pos = 0)
|
169
|
+
result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
|
170
170
|
raise EOFError unless result
|
171
171
|
|
172
172
|
str
|
@@ -218,11 +218,11 @@ end
|
|
218
218
|
|
219
219
|
class ::UNIXSocket
|
220
220
|
def recv(maxlen, flags = 0, outbuf = nil)
|
221
|
-
Polyphony.backend_recv(self, outbuf || +'', maxlen)
|
221
|
+
Polyphony.backend_recv(self, outbuf || +'', maxlen, 0)
|
222
222
|
end
|
223
223
|
|
224
|
-
def recv_loop(&block)
|
225
|
-
Polyphony.backend_recv_loop(self, &block)
|
224
|
+
def recv_loop(maxlen = 8192, &block)
|
225
|
+
Polyphony.backend_recv_loop(self, maxlen, &block)
|
226
226
|
end
|
227
227
|
alias_method :read_loop, :recv_loop
|
228
228
|
|
@@ -242,8 +242,8 @@ class ::UNIXSocket
|
|
242
242
|
Polyphony.backend_send(self, mesg, 0)
|
243
243
|
end
|
244
244
|
|
245
|
-
def readpartial(maxlen, str = +'')
|
246
|
-
result = Polyphony.backend_recv(self, str, maxlen)
|
245
|
+
def readpartial(maxlen, str = +'', buffer_pos = 0)
|
246
|
+
result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
|
247
247
|
raise EOFError unless result
|
248
248
|
|
249
249
|
str
|