Skip to content

Commit 15a7d71

Browse files
committed
Merge pull request #39 from ruby-debug/sub-proc-init-race
Sub proc init race
2 parents d13955e + 2064c9a commit 15a7d71

File tree

4 files changed

+59
-52
lines changed

4 files changed

+59
-52
lines changed

lib/ruby-debug-ide.rb

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
require 'stringio'
33
require "socket"
44
require 'thread'
5-
if (RUBY_VERSION < '2.0' || defined?(JRUBY_VERSION))
5+
if RUBY_VERSION < '2.0' || defined?(JRUBY_VERSION)
66
require 'ruby-debug-base'
77
else
88
require 'debase'
@@ -59,20 +59,20 @@ def interrupt_last
5959
end
6060
end
6161

62-
def start_server(host = nil, port = 1234)
62+
def start_server(host = nil, port = 1234, notify_dispatcher = false)
6363
return if started?
6464
start
65-
start_control(host, port)
65+
start_control(host, port, notify_dispatcher)
6666
end
6767

6868
def prepare_debugger(options)
69-
start_server(options.host, options.port)
70-
71-
raise "Control thread did not start (#{@control_thread}}" unless @control_thread && @control_thread.alive?
72-
7369
@mutex = Mutex.new
7470
@proceed = ConditionVariable.new
7571

72+
start_server(options.host, options.port, options.notify_dispatcher)
73+
74+
raise "Control thread did not start (#{@control_thread}}" unless @control_thread && @control_thread.alive?
75+
7676
# wait for 'start' command
7777
@mutex.synchronize do
7878
@proceed.wait(@mutex)
@@ -97,18 +97,20 @@ def run_prog_script
9797
end
9898
end
9999

100-
def start_control(host, port)
100+
def start_control(host, port, notify_dispatcher)
101101
raise "Debugger is not started" unless started?
102102
return if @control_thread
103103
@control_thread = DebugThread.new do
104104
begin
105105
# 127.0.0.1 seemingly works with all systems and with IPv6 as well.
106106
# "localhost" and nil have problems on some systems.
107107
host ||= '127.0.0.1'
108-
gem_name = (defined?(JRUBY_VERSION) || RUBY_VERSION < '1.9.0') ? 'ruby-debug-base' :
109-
RUBY_VERSION < '2.0.0' ? 'ruby-debug-base19x' : 'debase'
110108
server = TCPServer.new(host, port)
109+
gem_name = (defined?(JRUBY_VERSION) || RUBY_VERSION < '1.9.0') ? 'ruby-debug-base' :
110+
RUBY_VERSION < '2.0.0' ? 'ruby-debug-base19x' : 'debase'
111111
$stderr.printf "Fast Debugger (ruby-debug-ide #{IDE_VERSION}, #{gem_name} #{VERSION}) listens on #{host}:#{port}\n"
112+
notify_dispatcher(port) if notify_dispatcher
113+
112114
while (session = server.accept)
113115
$stderr.puts "Connected from #{session.peeraddr[2]}" if Debugger.cli_debug
114116
dispatcher = ENV['IDE_PROCESS_DISPATCHER']
@@ -122,7 +124,7 @@ def start_control(host, port)
122124
IdeControlCommandProcessor.new(interface).process_commands
123125
rescue StandardError, ScriptError => ex
124126
bt = ex.backtrace
125-
$stderr.printf "#{Process.pid}: Exception in DebugThread loop: #{ex.message}\nBacktrace:\n#{bt ? bt.join("\n from: ") : "<none>"}\n"
127+
$stderr.printf "#{Process.pid}: Exception in DebugThread loop: #{ex.message}(#{ex.class})\nBacktrace:\n#{bt ? bt.join("\n from: ") : "<none>"}\n"
126128
exit 1
127129
end
128130
end
@@ -134,6 +136,31 @@ def start_control(host, port)
134136
end
135137
end
136138

139+
private
140+
141+
def notify_dispatcher(port)
142+
return unless ENV['IDE_PROCESS_DISPATCHER']
143+
acceptor_host, acceptor_port = ENV['IDE_PROCESS_DISPATCHER'].split(":")
144+
acceptor_host, acceptor_port = '127.0.0.1', acceptor_host unless acceptor_port
145+
146+
connected = false
147+
3.times do |i|
148+
begin
149+
s = TCPSocket.open(acceptor_host, acceptor_port)
150+
s.print(port)
151+
s.close
152+
connected = true
153+
print_debug "Ide process dispatcher notified about sub-debugger which listens on #{port}\n"
154+
return
155+
rescue => bt
156+
$stderr.puts "#{Process.pid}: connection failed(#{i+1})"
157+
$stderr.puts "Exception: #{bt}"
158+
$stderr.puts bt.backtrace.map { |l| "\t#{l}" }.join("\n")
159+
sleep 0.3
160+
end unless connected
161+
end
162+
end
163+
137164
end
138165

139166
class Exception # :nodoc:

lib/ruby-debug-ide/ide_processor.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def process_commands
4747
end
4848
state.restore_context
4949
end
50-
rescue IOError, Errno::EPIPE
50+
rescue IOError, SystemCallError
5151
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
5252
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
5353
rescue Exception
@@ -93,10 +93,12 @@ def process_commands
9393
end
9494
end
9595
end
96-
rescue IOError, Errno::EPIPE
96+
rescue IOError, SystemCallError
97+
@printer.print_debug "INTERNAL ERROR!!! #{$!}\n"
9798
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
9899
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
99100
rescue Exception
101+
@printer.print_debug "INTERNAL ERROR!!! #{$!}\n" rescue nil
100102
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
101103
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
102104
ensure

lib/ruby-debug-ide/interface.rb

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
11
require 'thread'
22

3-
class TCPSocket
4-
5-
# Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
6-
def non_blocking_gets
7-
loop do
8-
result, _, _ = IO.select( [self], nil, nil, 0.2 )
9-
next unless result
10-
return result[0].gets
11-
end
12-
end
13-
14-
end
15-
16-
module Debugger
3+
module Debugger
174
class Interface
185
end
196

@@ -23,15 +10,14 @@ class LocalInterface < Interface
2310

2411
class RemoteInterface < Interface # :nodoc:
2512
attr_accessor :command_queue
26-
attr_accessor :socket
2713

2814
def initialize(socket)
2915
@socket = socket
3016
@command_queue = Queue.new
3117
end
3218

3319
def read_command
34-
result = @socket.non_blocking_gets
20+
result = non_blocking_gets
3521
raise IOError unless result
3622
result.chomp
3723
end
@@ -42,9 +28,18 @@ def print(*args)
4228

4329
def close
4430
@socket.close
45-
rescue Exception
31+
rescue IOError
4632
end
47-
33+
34+
# Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
35+
def non_blocking_gets
36+
loop do
37+
result, _, _ = IO.select( [@socket], nil, nil, 0.2 )
38+
next unless result
39+
return result[0].gets
40+
end
41+
end
42+
4843
end
4944

5045
end

lib/ruby-debug-ide/multiprocess/pre_child.rb

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,19 @@ def pre_child
1717
'stop' => false,
1818
'tracing' => false,
1919
'int_handler' => true,
20-
'cli_debug' => (ENV['DEBUGGER_CLI_DEBUG'] == 'true')
20+
'cli_debug' => (ENV['DEBUGGER_CLI_DEBUG'] == 'true'),
21+
'notify_dispatcher' => true
2122
)
2223

23-
acceptor_host, acceptor_port = ENV['IDE_PROCESS_DISPATCHER'].split(":")
24-
acceptor_host, acceptor_port = '127.0.0.1', acceptor_host unless acceptor_port
25-
26-
connected = false
27-
3.times do |i|
28-
begin
29-
s = TCPSocket.open(acceptor_host, acceptor_port)
30-
s.print(port)
31-
s.close
32-
connected = true
33-
start_debugger(options)
34-
return
35-
rescue => bt
36-
$stderr.puts "#{Process.pid}: connection failed(#{i+1})"
37-
$stderr.puts "Exception: #{bt}"
38-
$stderr.puts bt.backtrace.map { |l| "\t#{l}" }.join("\n")
39-
sleep 0.3
40-
end unless connected
41-
end
24+
start_debugger(options)
4225
end
4326

4427
def start_debugger(options)
4528
if Debugger.started?
4629
# we're in forked child, only need to restart control thread
4730
Debugger.breakpoints.clear
4831
Debugger.control_thread = nil
49-
Debugger.start_control(options.host, options.port)
32+
Debugger.start_control(options.host, options.port, options.notify_dispatcher)
5033
end
5134

5235
if options.int_handler

0 commit comments

Comments
 (0)