def initialize opts = {}, &block
getopt = getopts opts
@obj = getopt['object']
@socket_creation_attempts = getopt['socket_creation_attempts'] || default('socket_creation_attempts')
@debug = getopt['debug'] || default('debug')
@psname = getopt['psname']
@at_exit = getopt['at_exit']
@dumped = getopt['dumped']
@threadsafe = getopt['threadsafe'] || default('threadsafe')
raise ArgumentError, 'no slave object or slave object block provided!' if
@obj.nil? and block.nil?
@shutdown = false
@waiter = @status = nil
@lifeline = LifeLine.new
init_failure = lambda do |e|
trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
o = Object.new
class << o
attr_accessor '__slave_object_failure__'
end
o.__slave_object_failure__ = Marshal.dump [e.class, e.message, e.backtrace]
@object = o
end
unless((@pid = Slave::fork))
e = nil
begin
Kernel.at_exit{ Kernel.exit! }
@lifeline.catch
if @obj
@object = @obj
else
begin
@object = block.call
rescue Exception => e
init_failure[e]
end
end
if block and @obj
begin
block[@obj]
rescue Exception => e
init_failure[e]
end
end
$0 = (@psname ||= gen_psname(@object))
unless @dumped or @object.respond_to?('__slave_object_failure__')
@object.extend DRbUndumped
end
if @threadsafe
@object = ThreadSafe.new @object
end
@ppid, @pid = Process::ppid, Process::pid
@socket = nil
@uri = nil
tmpdir, basename = Dir::tmpdir, File::basename(@psname)
@socket_creation_attempts.times do |attempt|
se = nil
begin
s = File::join(tmpdir, "#{ basename }_#{ attempt }_#{ rand }")
u = "drbunix://#{ s }"
DRb::start_service u, @object
@socket = s
@uri = u
trace{ "child - socket <#{ @socket }>" }
trace{ "child - uri <#{ @uri }>" }
break
rescue Errno::EADDRINUSE => se
nil
end
end
if @socket and @uri
trap('SIGUSR2') do
DBb::thread.kill rescue nil
FileUtils::rm_f @socket rescue nil
exit
end
@lifeline.puts @socket
@lifeline.cling
else
@lifeline.release
warn "slave(#{ $$ }) could not create socket!"
exit
end
rescue Exception => e
trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
ensure
status = e.respond_to?('status') ? e.status : 1
exit(status)
end
else
detach
@lifeline.throw
buf = @lifeline.gets
raise "failed to find slave socket" if buf.nil? or buf.strip.empty?
@socket = buf.strip
trace{ "parent - socket <#{ @socket }>" }
if @at_exit
@at_exit_thread = @lifeline.on_cut{
@at_exit.respond_to?('call') ? @at_exit.call(self) : send(@at_exit.to_s, self)
}
end
if @socket and File::exist? @socket
Kernel.at_exit{ FileUtils::rm_f @socket }
@uri = "drbunix://#{ socket }"
trace{ "parent - uri <#{ @uri }>" }
DRb::start_service('druby://localhost:0', nil) unless DRb::thread
@object = DRbObject::new nil, @uri
if @object.respond_to? '__slave_object_failure__'
c, m, bt = Marshal.load @object.__slave_object_failure__
(e = c.new(m)).set_backtrace bt
trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
raise e
end
@psname ||= gen_psname(@object)
else
raise "failed to find slave socket <#{ @socket }>"
end
end
end