polyphony 0.67 → 0.68
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/bin/pdbg +91 -9
- data/ext/polyphony/backend_io_uring.c +1 -1
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/debugger.rb +225 -0
- data/lib/polyphony/extensions/fiber.rb +24 -38
- data/lib/polyphony/extensions/io.rb +0 -2
- data/lib/polyphony/extensions/openssl.rb +62 -0
- data/lib/polyphony/extensions/socket.rb +2 -2
- data/lib/polyphony/net.rb +1 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +0 -1
- data/test/test_fiber.rb +2 -2
- data/test/test_supervise.rb +24 -24
- metadata +4 -4
- data/lib/polyphony/debugger/server.rb +0 -137
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d23a9c0f60530fdee1d367f34359e4f694bd4a42ba3c0112e61433df05c29a81
|
4
|
+
data.tar.gz: bf75e3f1a633f5c94a43fd45a2de2545d87b1e751753980abb8e1d3eddd4eade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b810c7ddbd383039e5f34fe76dbea089807417d4415e79a952f6d6a46538cf99c55ae7a34f3eadf61d22af23ac29a0910c62ea5c97dff6e86b94083d98444edf
|
7
|
+
data.tar.gz: '063459edd8f669a024e0b7b8f1daa47983db4cf9ac7f3ca81a69359de92eace4831d843e9b74645db08a50c26d83a2878a5fdb6b125adc260afd13a3135e710c'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.68 2021-08-13
|
2
|
+
|
3
|
+
- Fix missing default value in socket classes' `#readpartial`
|
4
|
+
- Fix linking of operations in `Backend#chain` (io_uring version)
|
5
|
+
- Rename `Fiber#attach` to `Fiber#attach_to`
|
6
|
+
- Expose original `SSLServer#accept`
|
7
|
+
|
1
8
|
## 0.67 2021-08-06
|
2
9
|
|
3
10
|
- Improve fiber monitoring
|
data/Gemfile.lock
CHANGED
data/bin/pdbg
CHANGED
@@ -8,23 +8,105 @@ UNIX_SOCKET_PATH = '/tmp/pdbg.sock'
|
|
8
8
|
|
9
9
|
cmd = ARGV.join(' ')
|
10
10
|
injected_lib_path = File.expand_path('../lib/polyphony/debugger/server_inject.rb', __dir__)
|
11
|
-
p cmd
|
12
11
|
pid = fork { exec("env POLYPHONY_DEBUG_SOCKET_PATH=#{UNIX_SOCKET_PATH} ruby #{cmd}") }
|
13
12
|
puts "Started debugged process (#{pid})"
|
14
13
|
|
15
|
-
|
16
|
-
socket
|
14
|
+
socket = nil
|
15
|
+
while !socket
|
16
|
+
socket = UNIXSocket.new(UNIX_SOCKET_PATH) rescue nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_command(cmd)
|
20
|
+
case cmd
|
21
|
+
when /^(step|s)$/
|
22
|
+
{ cmd: :step }
|
23
|
+
when /^(state|st)$/
|
24
|
+
{ cmd: :state }
|
25
|
+
when /^(help|h)$/
|
26
|
+
{ cmd: :help }
|
27
|
+
when /^(list|l)$/
|
28
|
+
{ cmd: :list }
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def display_info(info)
|
35
|
+
info = eval(info)
|
36
|
+
case (info && info[:kind])
|
37
|
+
when :listing
|
38
|
+
print_listing(info)
|
39
|
+
when :state
|
40
|
+
print_state(info)
|
41
|
+
else
|
42
|
+
p info
|
43
|
+
end
|
44
|
+
rescue SyntaxError
|
45
|
+
puts "Failed to eval:"
|
46
|
+
p info
|
47
|
+
end
|
48
|
+
|
49
|
+
FILE_LINES_CACHE = {}
|
50
|
+
|
51
|
+
def self.get_snippet(path, lineno)
|
52
|
+
lines = FILE_LINES_CACHE[path] ||= IO.read(path).lines
|
53
|
+
start_idx = lineno - 5
|
54
|
+
stop_idx = lineno + 3
|
55
|
+
stop_idx = lines.size - 1 if stop_idx >= lines.size
|
56
|
+
start_idx = 0 if start_idx < 0
|
57
|
+
(start_idx..stop_idx).map { |idx| [idx + 1, lines[idx]]}
|
58
|
+
end
|
59
|
+
|
60
|
+
def print_snippet(info, snippet, cur_line)
|
61
|
+
places = FILE_LINES_CACHE[info[:path]].size.to_s.size
|
62
|
+
snippet.each do |(lineno, line)|
|
63
|
+
is_cur = lineno == cur_line
|
64
|
+
formatted = format("%s% #{places}d %s", is_cur ? '=> ' : ' ', lineno, line)
|
65
|
+
puts formatted
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def print_listing(info)
|
70
|
+
snippet = get_snippet(info[:path], info[:lineno])
|
71
|
+
puts "Fiber: #{info[:fiber]} Location: #{info[:path]}:#{info[:lineno]}"
|
72
|
+
puts
|
73
|
+
print_snippet(info, snippet, info[:lineno])
|
74
|
+
puts
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_help
|
78
|
+
puts
|
79
|
+
puts "Here's some help..."
|
80
|
+
puts
|
81
|
+
end
|
82
|
+
|
83
|
+
def print_state(info)
|
84
|
+
p info
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_user_cmd
|
88
|
+
while true
|
89
|
+
STDOUT << "(pdbg) "
|
90
|
+
cmd = parse_command(STDIN.gets)
|
91
|
+
next unless cmd
|
92
|
+
|
93
|
+
if cmd[:cmd] == :help
|
94
|
+
print_help
|
95
|
+
else
|
96
|
+
return cmd if cmd
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
17
101
|
socket.puts 'pdbg'
|
18
102
|
response = socket.gets
|
19
103
|
if response.chomp == 'pdbg'
|
20
104
|
puts 'Connected to process'
|
21
105
|
end
|
22
106
|
loop do
|
23
|
-
|
24
|
-
|
107
|
+
info = socket.gets.chomp
|
108
|
+
display_info(info)
|
25
109
|
|
26
|
-
|
27
|
-
cmd
|
28
|
-
puts '-' * 40
|
29
|
-
socket.puts cmd
|
110
|
+
cmd = get_user_cmd
|
111
|
+
socket.puts cmd.inspect
|
30
112
|
end
|
@@ -1248,7 +1248,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1248
1248
|
}
|
1249
1249
|
|
1250
1250
|
io_uring_sqe_set_data(last_sqe, ctx);
|
1251
|
-
unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC
|
1251
|
+
unsigned int flags = (i == (argc - 1)) ? IOSQE_ASYNC : IOSQE_ASYNC | IOSQE_IO_LINK;
|
1252
1252
|
io_uring_sqe_set_flags(last_sqe, flags);
|
1253
1253
|
sqe_count++;
|
1254
1254
|
}
|
data/lib/polyphony.rb
CHANGED
@@ -124,6 +124,6 @@ Polyphony.install_at_exit_handler
|
|
124
124
|
|
125
125
|
if (debug_socket_path = ENV['POLYPHONY_DEBUG_SOCKET_PATH'])
|
126
126
|
puts "Starting debug server on #{debug_socket_path}"
|
127
|
-
require 'polyphony/debugger
|
128
|
-
Polyphony
|
127
|
+
require 'polyphony/debugger'
|
128
|
+
Polyphony.start_debug_server(debug_socket_path)
|
129
129
|
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'polyphony/extensions/debug'
|
4
|
+
|
5
|
+
module Polyphony
|
6
|
+
TP_EVENTS = [
|
7
|
+
:line,
|
8
|
+
:call,
|
9
|
+
:return,
|
10
|
+
:b_call,
|
11
|
+
:b_return
|
12
|
+
]
|
13
|
+
|
14
|
+
def self.start_debug_server(socket_path)
|
15
|
+
server = DebugServer.new(socket_path)
|
16
|
+
controller = DebugController.new(server)
|
17
|
+
trace = TracePoint.new(*TP_EVENTS) { |tp| controller.handle_tp(trace, tp) }
|
18
|
+
trace.enable
|
19
|
+
|
20
|
+
at_exit do
|
21
|
+
Kernel.trace "program terminated"
|
22
|
+
trace.disable
|
23
|
+
server.stop
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class DebugController
|
28
|
+
def initialize(server)
|
29
|
+
@server = server
|
30
|
+
@server.wait_for_client
|
31
|
+
@state = { fibers: {} }
|
32
|
+
@control_fiber = Fiber.new { |f| control_loop(f) }
|
33
|
+
@control_fiber.transfer Fiber.current
|
34
|
+
end
|
35
|
+
|
36
|
+
def control_loop(source_fiber)
|
37
|
+
@peer = source_fiber
|
38
|
+
cmd = { cmd: :initial }
|
39
|
+
loop do
|
40
|
+
cmd = send(:"cmd_#{cmd[:cmd]}", cmd)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
POLYPHONY_LIB_DIR = File.expand_path('..', __dir__)
|
45
|
+
|
46
|
+
def get_next_trace_event
|
47
|
+
@peer.transfer.tap { |e| update_state(e) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def update_state(event)
|
51
|
+
trace update_state: event
|
52
|
+
@state[:fiber] = event[:fiber]
|
53
|
+
@state[:path] = event[:path]
|
54
|
+
@state[:lineno] = event[:lineno]
|
55
|
+
update_fiber_state(event)
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_fiber_state(event)
|
59
|
+
fiber_state = @state[:fibers][event[:fiber]] ||= { stack: [] }
|
60
|
+
case event[:kind]
|
61
|
+
when :call, :c_call, :b_call
|
62
|
+
fiber_state[:stack] << event
|
63
|
+
when :return, :c_return, :b_return
|
64
|
+
fiber_state[:stack].pop
|
65
|
+
end
|
66
|
+
fiber_state[:binding] = event[:binding]
|
67
|
+
fiber_state[:path] = event[:path]
|
68
|
+
fiber_state[:lineno] = event[:lineno]
|
69
|
+
end
|
70
|
+
|
71
|
+
def state_presentation(state)
|
72
|
+
{
|
73
|
+
fiber: fiber_id(state[:fiber]),
|
74
|
+
path: state[:path],
|
75
|
+
lineno: state[:lineno]
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def fiber_id(fiber)
|
80
|
+
{
|
81
|
+
object_id: fiber.object_id,
|
82
|
+
tag: fiber.tag
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def fiber_representation(fiber)
|
87
|
+
{
|
88
|
+
object_id: fiber.object_id,
|
89
|
+
tag: fiber.tag,
|
90
|
+
parent: fiber.parent && fiber_id(fiber.parent),
|
91
|
+
children: fiber.children.map { |c| fiber_id(c) }
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_next_command(info)
|
96
|
+
@server.get_command(info)
|
97
|
+
end
|
98
|
+
|
99
|
+
def cmd_initial(cmd)
|
100
|
+
get_next_command(nil)
|
101
|
+
end
|
102
|
+
|
103
|
+
def info_listing(state)
|
104
|
+
{
|
105
|
+
kind: :listing,
|
106
|
+
fiber: fiber_id(state[:fiber]),
|
107
|
+
path: state[:path],
|
108
|
+
lineno: state[:lineno]
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def info_state(state)
|
113
|
+
info_listing(state).merge(
|
114
|
+
kind: :state,
|
115
|
+
fibers: info_fiber_states(state[:fibers])
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
def info_fiber_states(fiber_states)
|
120
|
+
fiber_states.inject({}) do |h, (f, s)|
|
121
|
+
h[fiber_id(f)] = {
|
122
|
+
stack: s[:stack].map { |e| { path: e[:path], lineno: e[:lineno] } }
|
123
|
+
}
|
124
|
+
h
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def cmd_step(cmd)
|
129
|
+
tp = nil
|
130
|
+
fiber = nil
|
131
|
+
while true
|
132
|
+
event = get_next_trace_event
|
133
|
+
@peer = event[:fiber]
|
134
|
+
if event[:kind] == :line && event[:path] !~ /#{POLYPHONY_LIB_DIR}/
|
135
|
+
return get_next_command(info_listing(@state))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
rescue => e
|
139
|
+
trace "Uncaught error: #{e.inspect}"
|
140
|
+
@trace&.disable
|
141
|
+
end
|
142
|
+
|
143
|
+
def cmd_help(cmd)
|
144
|
+
get_next_command(kind: :help)
|
145
|
+
end
|
146
|
+
|
147
|
+
def cmd_list(cmd)
|
148
|
+
get_next_command(info_listing(@state))
|
149
|
+
end
|
150
|
+
|
151
|
+
def cmd_state(cmd)
|
152
|
+
get_next_command(info_state(@state))
|
153
|
+
end
|
154
|
+
|
155
|
+
def handle_tp(trace, tp)
|
156
|
+
return if Thread.current == @server.thread
|
157
|
+
return if Fiber.current == @control_fiber
|
158
|
+
|
159
|
+
kind = tp.event
|
160
|
+
event = {
|
161
|
+
fiber: Fiber.current,
|
162
|
+
kind: kind,
|
163
|
+
path: tp.path,
|
164
|
+
lineno: tp.lineno,
|
165
|
+
binding: tp.binding
|
166
|
+
}
|
167
|
+
case kind
|
168
|
+
when :call, :c_call, :b_call
|
169
|
+
event[:method_id] = tp.method_id
|
170
|
+
event[:parameters] = tp.parameters
|
171
|
+
when :return, :c_return, :b_return
|
172
|
+
event[:method_id] = tp.method_id
|
173
|
+
event[:return_value] = tp.return_value
|
174
|
+
end
|
175
|
+
@control_fiber.transfer(event)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class DebugServer
|
180
|
+
attr_reader :thread
|
181
|
+
|
182
|
+
def initialize(socket_path)
|
183
|
+
@socket_path = socket_path
|
184
|
+
@fiber = Fiber.current
|
185
|
+
start_server_thread
|
186
|
+
end
|
187
|
+
|
188
|
+
def start_server_thread
|
189
|
+
@thread = Thread.new do
|
190
|
+
puts("Listening on #{@socket_path}")
|
191
|
+
FileUtils.rm(@socket_path) if File.exists?(@socket_path)
|
192
|
+
socket = UNIXServer.new(@socket_path)
|
193
|
+
loop do
|
194
|
+
@client = socket.accept
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def stop
|
200
|
+
@thread.kill
|
201
|
+
end
|
202
|
+
|
203
|
+
def handle_client(client)
|
204
|
+
@client = client
|
205
|
+
end
|
206
|
+
|
207
|
+
def wait_for_client
|
208
|
+
sleep 0.1 until @client
|
209
|
+
msg = @client.gets
|
210
|
+
@client.puts msg
|
211
|
+
end
|
212
|
+
|
213
|
+
def get_command(info)
|
214
|
+
@client&.orig_write "#{info.inspect}\n"
|
215
|
+
cmd = @client&.orig_gets&.chomp
|
216
|
+
eval(cmd)
|
217
|
+
rescue SystemCallError
|
218
|
+
nil
|
219
|
+
rescue => e
|
220
|
+
trace "Error in interact_with_client: #{e.inspect}"
|
221
|
+
e.backtrace[0..3].each { |l| trace l }
|
222
|
+
@client = nil
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -79,47 +79,26 @@ module Polyphony
|
|
79
79
|
|
80
80
|
# Fiber supervision
|
81
81
|
module FiberSupervision
|
82
|
-
def supervise(opts
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
87
|
-
while true
|
88
|
-
supervise_perform(opts)
|
89
|
-
end
|
90
|
-
rescue Polyphony::MoveOn
|
91
|
-
# generated in #supervise_perform to stop supervisor
|
92
|
-
ensure
|
93
|
-
@on_child_done = nil
|
94
|
-
end
|
82
|
+
def supervise(*fibers, **opts, &block)
|
83
|
+
block ||= opts[:on_done] ||
|
84
|
+
(opts[:on_error] && supervise_on_error_proc(opts[:on_error]))
|
85
|
+
raise "No block given" unless block
|
95
86
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
restart_fiber(fiber, opts)
|
100
|
-
elsif Fiber.current.children.empty?
|
101
|
-
Fiber.current.stop
|
87
|
+
fibers.each do |f|
|
88
|
+
f.attach_to(self) unless f.parent == self
|
89
|
+
f.monitor(self)
|
102
90
|
end
|
103
|
-
rescue Polyphony::Restart
|
104
|
-
restart_all_children
|
105
|
-
rescue Exception => e
|
106
|
-
Kernel.raise e if e.source_fiber.nil? || e.source_fiber == self
|
107
91
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
92
|
+
mailbox = monitor_mailbox
|
93
|
+
|
94
|
+
while true
|
95
|
+
(fiber, result) = mailbox.shift
|
96
|
+
block&.call(fiber, result)
|
112
97
|
end
|
113
98
|
end
|
114
99
|
|
115
|
-
def
|
116
|
-
opts[:
|
117
|
-
case opts[:restart]
|
118
|
-
when true
|
119
|
-
fiber.restart
|
120
|
-
when :one_for_all
|
121
|
-
@children.keys.each(&:restart)
|
122
|
-
end
|
100
|
+
def supervise_on_error_proc(on_error)
|
101
|
+
->(f, r) { opts[:on_error].(f, r) if r.is_a?(Exception) }
|
123
102
|
end
|
124
103
|
end
|
125
104
|
|
@@ -261,10 +240,17 @@ module Polyphony
|
|
261
240
|
@parent.add_child(self)
|
262
241
|
end
|
263
242
|
|
264
|
-
def
|
243
|
+
def attach_to(fiber)
|
265
244
|
@parent.remove_child(self)
|
266
|
-
@parent =
|
267
|
-
|
245
|
+
@parent = fiber
|
246
|
+
fiber.add_child(self)
|
247
|
+
end
|
248
|
+
|
249
|
+
def attach_and_monitor(fiber)
|
250
|
+
@parent.remove_child(self)
|
251
|
+
@parent = fiber
|
252
|
+
fiber.add_child(self)
|
253
|
+
monitor(fiber)
|
268
254
|
end
|
269
255
|
end
|
270
256
|
|
@@ -114,6 +114,68 @@ end
|
|
114
114
|
|
115
115
|
# OpenSSL socket helper methods (to make it compatible with Socket API) and overrides
|
116
116
|
class ::OpenSSL::SSL::SSLServer
|
117
|
+
attr_reader :ctx
|
118
|
+
|
119
|
+
alias_method :orig_accept, :accept
|
120
|
+
def accept
|
121
|
+
# when @ctx.servername_cb is set, we use a worker thread to run the
|
122
|
+
# ssl.accept call. We need to do this because:
|
123
|
+
# - We cannot switch fibers inside of the servername_cb proc (see
|
124
|
+
# https://github.com/ruby/openssl/issues/415)
|
125
|
+
# - We don't want to stop the world while we're busy provisioning an ACME
|
126
|
+
# certificate
|
127
|
+
if @use_accept_worker.nil?
|
128
|
+
if (@use_accept_worker = use_accept_worker_thread?)
|
129
|
+
start_accept_worker_thread
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
sock, = @svr.accept
|
134
|
+
begin
|
135
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
|
136
|
+
ssl.sync_close = true
|
137
|
+
if @use_accept_worker
|
138
|
+
@accept_worker_fiber << [ssl, Fiber.current]
|
139
|
+
receive
|
140
|
+
else
|
141
|
+
ssl.accept
|
142
|
+
end
|
143
|
+
ssl
|
144
|
+
rescue Exception => ex
|
145
|
+
if ssl
|
146
|
+
ssl.close
|
147
|
+
else
|
148
|
+
sock.close
|
149
|
+
end
|
150
|
+
raise ex
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def start_accept_worker_thread
|
155
|
+
fiber = Fiber.current
|
156
|
+
@accept_worker_thread = Thread.new do
|
157
|
+
fiber << Fiber.current
|
158
|
+
loop do
|
159
|
+
socket, peer = receive
|
160
|
+
socket.accept
|
161
|
+
peer << socket
|
162
|
+
rescue => e
|
163
|
+
peer.schedule(e) if fiber
|
164
|
+
end
|
165
|
+
end
|
166
|
+
@accept_worker_fiber = receive
|
167
|
+
end
|
168
|
+
|
169
|
+
def use_accept_worker_thread?
|
170
|
+
!!@ctx.servername_cb
|
171
|
+
end
|
172
|
+
|
173
|
+
alias_method :orig_close, :close
|
174
|
+
def close
|
175
|
+
@accept_worker_thread&.kill
|
176
|
+
orig_close
|
177
|
+
end
|
178
|
+
|
117
179
|
def accept_loop(ignore_errors = true)
|
118
180
|
loop do
|
119
181
|
yield accept
|
@@ -206,7 +206,7 @@ class ::TCPSocket
|
|
206
206
|
# Polyphony.backend_send(self, mesg, 0)
|
207
207
|
# end
|
208
208
|
|
209
|
-
def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof)
|
209
|
+
def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof = true)
|
210
210
|
result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
|
211
211
|
raise EOFError if !result && raise_on_eof
|
212
212
|
result
|
@@ -299,7 +299,7 @@ class ::UNIXSocket
|
|
299
299
|
Polyphony.backend_send(self, mesg, 0)
|
300
300
|
end
|
301
301
|
|
302
|
-
def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof)
|
302
|
+
def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof = true)
|
303
303
|
result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
|
304
304
|
raise EOFError if !result && raise_on_eof
|
305
305
|
result
|
data/lib/polyphony/net.rb
CHANGED
data/lib/polyphony/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -47,7 +47,6 @@ class MiniTest::Test
|
|
47
47
|
def setup
|
48
48
|
# trace "* setup #{self.name}"
|
49
49
|
Fiber.current.setup_main_fiber
|
50
|
-
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
51
50
|
Thread.current.backend.finalize
|
52
51
|
Thread.current.backend = Polyphony::Backend.new
|
53
52
|
sleep 0.001
|
data/test/test_fiber.rb
CHANGED
@@ -791,7 +791,7 @@ class FiberTest < MiniTest::Test
|
|
791
791
|
], buf
|
792
792
|
end
|
793
793
|
|
794
|
-
def
|
794
|
+
def test_attach_to
|
795
795
|
buf = []
|
796
796
|
child = nil
|
797
797
|
parent = spin(:parent) do
|
@@ -809,7 +809,7 @@ class FiberTest < MiniTest::Test
|
|
809
809
|
|
810
810
|
snooze
|
811
811
|
assert_equal parent, child.parent
|
812
|
-
child.
|
812
|
+
child.attach_to(new_parent)
|
813
813
|
assert_equal new_parent, child.parent
|
814
814
|
parent.await
|
815
815
|
|
data/test/test_supervise.rb
CHANGED
@@ -2,29 +2,29 @@
|
|
2
2
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
5
|
+
class SuperviseTest < MiniTest::Test
|
6
|
+
def test_supervise_with_no_arguments
|
7
|
+
assert_raises(RuntimeError) do
|
8
|
+
supervise
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_supervise_with_block
|
13
|
+
buffer = []
|
14
|
+
f1 = spin(:f1) { receive }
|
15
|
+
f2 = spin(:f2) { receive }
|
16
|
+
supervisor = spin(:supervisor) { supervise(f1, f2) { |*args| buffer << args } }
|
17
|
+
|
18
|
+
snooze
|
19
|
+
f1 << 'foo'
|
20
|
+
f1.await
|
21
|
+
10.times { snooze }
|
22
|
+
assert_equal [[f1, 'foo']], buffer
|
23
|
+
|
24
|
+
f2 << 'bar'
|
25
|
+
f2.await
|
26
|
+
assert_equal [[f1, 'foo'], [f2, 'bar']], buffer
|
27
|
+
end
|
28
28
|
|
29
29
|
# def test_supervise_with_restart
|
30
30
|
# watcher = spin { receive }
|
@@ -101,4 +101,4 @@ require_relative 'helper'
|
|
101
101
|
# assert :dead, f.state
|
102
102
|
# assert :dead, p.state
|
103
103
|
# end
|
104
|
-
|
104
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.68'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -357,7 +357,7 @@ files:
|
|
357
357
|
- lib/polyphony/core/thread_pool.rb
|
358
358
|
- lib/polyphony/core/throttler.rb
|
359
359
|
- lib/polyphony/core/timer.rb
|
360
|
-
- lib/polyphony/debugger
|
360
|
+
- lib/polyphony/debugger.rb
|
361
361
|
- lib/polyphony/extensions/core.rb
|
362
362
|
- lib/polyphony/extensions/debug.rb
|
363
363
|
- lib/polyphony/extensions/fiber.rb
|
@@ -421,7 +421,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
421
421
|
- !ruby/object:Gem::Version
|
422
422
|
version: '0'
|
423
423
|
requirements: []
|
424
|
-
rubygems_version: 3.1.
|
424
|
+
rubygems_version: 3.1.4
|
425
425
|
signing_key:
|
426
426
|
specification_version: 4
|
427
427
|
summary: Fine grained concurrency for Ruby
|
@@ -1,137 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'polyphony/extensions/debug'
|
4
|
-
|
5
|
-
module Polyphony
|
6
|
-
class DebugServer
|
7
|
-
TP_EVENTS = [
|
8
|
-
:line,
|
9
|
-
:call,
|
10
|
-
:return,
|
11
|
-
:b_call,
|
12
|
-
:b_return
|
13
|
-
]
|
14
|
-
|
15
|
-
|
16
|
-
def self.start(socket_path)
|
17
|
-
server = self.new(socket_path)
|
18
|
-
server.start
|
19
|
-
|
20
|
-
trace = TracePoint.new(*TP_EVENTS) { |tp| server.handle_tp(trace, tp) }
|
21
|
-
trace.enable
|
22
|
-
|
23
|
-
at_exit do
|
24
|
-
puts "program terminated"
|
25
|
-
trace.disable
|
26
|
-
server.stop
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(socket_path)
|
31
|
-
@socket_path = socket_path
|
32
|
-
@fiber = Fiber.current
|
33
|
-
@controller = spin { control_loop }
|
34
|
-
puts "@fiber: #{@fiber.inspect}"
|
35
|
-
end
|
36
|
-
|
37
|
-
def start
|
38
|
-
fiber = Fiber.current
|
39
|
-
@server = spin(:pdbg_server) do
|
40
|
-
puts("Listening on #{@socket_path}")
|
41
|
-
FileUtils.rm(@socket_path) if File.exists?(@socket_path)
|
42
|
-
socket = UNIXServer.new(@socket_path)
|
43
|
-
fiber << :ready
|
44
|
-
id = 0
|
45
|
-
socket.accept_loop do |client|
|
46
|
-
puts "accepted connection"
|
47
|
-
handle_client(client)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
receive
|
51
|
-
end
|
52
|
-
|
53
|
-
def stop
|
54
|
-
@server.terminate
|
55
|
-
@controller.terminate
|
56
|
-
end
|
57
|
-
|
58
|
-
POLYPHONY_LIB_DIR = File.expand_path('../..', __dir__)
|
59
|
-
def handle_client(client)
|
60
|
-
@client = client
|
61
|
-
puts "trace enabled"
|
62
|
-
end
|
63
|
-
|
64
|
-
def control_loop
|
65
|
-
@cmd = :step
|
66
|
-
loop do
|
67
|
-
case @cmd
|
68
|
-
when :step
|
69
|
-
step
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def step
|
75
|
-
tp = nil
|
76
|
-
fiber = nil
|
77
|
-
while true
|
78
|
-
event = receive
|
79
|
-
fiber = event[:fiber]
|
80
|
-
if fiber == @fiber && event[:kind] == :line && event[:path] !~ /#{POLYPHONY_LIB_DIR}/
|
81
|
-
interact_with_client(event)
|
82
|
-
fiber << :ok
|
83
|
-
fiber.__unpark__
|
84
|
-
return
|
85
|
-
end
|
86
|
-
|
87
|
-
fiber << :ok
|
88
|
-
fiber.__unpark__
|
89
|
-
end
|
90
|
-
rescue => e
|
91
|
-
puts "Uncaught error: #{e.inspect}"
|
92
|
-
@trace&.disable
|
93
|
-
@client = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
def interact_with_client(event)
|
97
|
-
@client.puts event.inspect
|
98
|
-
result = @client.gets&.chomp
|
99
|
-
end
|
100
|
-
|
101
|
-
def handle_tp(trace, tp)
|
102
|
-
return if @in_handle_tp
|
103
|
-
|
104
|
-
process_tp(trace, tp)
|
105
|
-
end
|
106
|
-
|
107
|
-
def process_tp(trace, tp)
|
108
|
-
@in_handle_tp = true
|
109
|
-
if !@client
|
110
|
-
wait_for_client
|
111
|
-
end
|
112
|
-
|
113
|
-
puts "- #{tp.event} #{tp.path}:#{tp.lineno}"
|
114
|
-
|
115
|
-
fiber = Fiber.current
|
116
|
-
fiber.__park__
|
117
|
-
|
118
|
-
@controller << {
|
119
|
-
fiber: fiber,
|
120
|
-
kind: tp.event,
|
121
|
-
path: tp.path,
|
122
|
-
lineno: tp.lineno
|
123
|
-
}
|
124
|
-
receive
|
125
|
-
ensure
|
126
|
-
@in_handle_tp = nil
|
127
|
-
end
|
128
|
-
|
129
|
-
def wait_for_client
|
130
|
-
puts "wait_for_client"
|
131
|
-
sleep 0.1 until @client
|
132
|
-
puts " got client!"
|
133
|
-
msg = @client.gets
|
134
|
-
@client.puts msg
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|