Fork process with DRb client

Hello,

If I fork a (Linux) process with a DRb client, both parent and child
will communicate with the DRb server using the same Unix Domain
sockets. With predictable, nasty results.

Is there a way for the child to somhow destroy its DRb::DRbObject and
create a new ?

I’ve scanned the source, but found nothing useful.

Any suggestions ?

Cheers,

Han H.

On Fri, 23 Jun 2006, Han H. wrote:

Any suggestions ?

Cheers,

Han H.

this seems ok:

harp:~ > cat a.rb
require ‘drb’
require ‘tmpdir’
require ‘yaml’

STDOUT.sync = STDERR.sync = true

setup unix domain drb server in another process

 r, w = IO.pipe
 uri = nil
 cid = fork

 unless cid
   r.close
   sok = File.join Dir.tmpdir, Process.pid.to_s
   at_exit{ File.unlink sok }
   uri = "drbunix://#{ sok }"
   (list = []).extend DRbUndumped
   DRb.start_service uri, 'children' => list, 'parent' => nil
   w.puts uri
   Thread.new(Thread.current) do |cur|
     begin
       w.puts "no zombies"
     rescue Exception
       cur.exit
     end
   end
   DRb.thread.join
 else
   w.close
   uri = r.gets.strip
 end

attach a client to it

 DRb.start_service
 dhash = DRbObject.new nil, uri
 dhash['parent'] = Process.pid

dump info

 y 'uri' => uri
 y 'parent' => dhash['parent']
 y 'children' => dhash['children'].map

fork a few children - each one tears down and sets up client again

 3.times do
   cid = fork
   unless cid
     DRb.stop_service
     DRb.start_service
     dhash = DRbObject.new nil, uri
     dhash['children'] << Process.pid
     exit
   else
     Process.wait
   end
 end

dump info

 y 'uri' => uri
 y 'parent' => dhash['parent']
 y 'children' => dhash['children'].map

harp:~ > ruby a.rb
uri: drbunix:///tmp/15163
parent: 15162
children: []

uri: drbunix:///tmp/15163
parent: 15162
children:

  • 15165
  • 15167
  • 15169

regards.

-a

On Sat, 24 Jun 2006, Han H. wrote:

hi han-

The problem with this approach is that you have to know the uri of the
server at fork time, which can be a problem.

it shouldn’t be, you can do, for example

uri = drbobject.__drburi

eg. if you have a drb object you always have it’s uri. if you have
it’s uri
you can always detach and re-attach. though i agree it’s not that
elegant.

(And I didn’t realize that you can destroy a DRbObject with
DRb.stop_service – I associate that with the server side of things).

yeah, it seems like it. remember though, every ruby program which uses
drb is
a servant - drb is always both server and client. fyi.

if f.nil?

This just deletes the commection pool in the child. Forking within the
mutex is because it’s possible to fork with some other thread having the
mutex locked, which causes problems for the one-thread child.

doesn’t it leave open file handles lying around?

But it’s a bit of a kludge and I would prefer it if DRbObject had a method
to deal with it.

agreed. it’s always tough to carry open file handles across a fork -
sometimes is just seems easier to code around it. for instance, by
setting up
a ring server where each service registers in the parent. then all
children
simply grab drbobjects of of that ring server. maybe something like
this
might apply to your problem:

harp:~ > ruby a.rb
[[:key, :val]]
[:val]

harp:~ > cat a.rb
require ‘drb’
require ‘rinda/ring’
require ‘rinda/tuplespace’
require ‘tmpdir’
require ‘yaml’

STDOUT.sync = STDERR.sync = true
def start_ring_server
Rinda::RingServer.new Rinda::TupleSpace.new
end
def find_ring_server
Rinda::TupleSpaceProxy.new Rinda::RingFinger::new.lookup_ring_any
end
def drb_join
begin; DRb.thread.join; rescue Exception; exit; end
end
children = []

one process starts a ring server. this could also be the parent.

other

processes, or even the parent, can register drb objects with this

ring

server.

 children <<
   fork{
     DRb.start_service
     rs = start_ring_server
     drb_join
   }

this one starts up a hash server and registers it’s location

 children <<
   fork{
     (dhash = {}).extend DRbUndumped
     DRb.start_service nil, dhash
     rs = find_ring_server
     rs.write [:dhash, dhash]
     drb_join
   }

this one starts up an array server and registers it’s location

 children <<
   fork{
     (darray = []).extend DRbUndumped
     DRb.start_service nil, darray
     rs = find_ring_server
     rs.write [:darray, darray]
     drb_join
   }

now any child, or the parent, can find and use these drb objects

without

knowing their locations or requiring a connection before forking -

only

the ‘name’ of the object need be known.

 Process.waitpid fork{
   DRb.start_service
   rs = find_ring_server

   tuple = rs.read [:dhash, nil]
   dhash = tuple.last
   dhash[:key] = :val

   tuple = rs.read [:darray, nil]
   darray = tuple.last
   darray << :val
 }

here the parent finds the dhash and darray to print them out

 DRb.start_service
 rs = find_ring_server

 tuple = rs.read [:dhash, nil]
 dhash = tuple.last

 tuple = rs.read [:darray, nil]
 darray = tuple.last

 p dhash.map  #=> [[:key, :val]]
 p darray.map #=> [:val]

wait for children

 at_exit{
   children.each{|cid| Process.kill 'TERM', cid rescue next}
   loop{ Process.wait Process::WNOHANG rescue break }
 }
 STDIN.gets

regards.

-a

On 6/23/06, [email protected] [email protected] wrote:

[ cut]

     exit
   else
     Process.wait

[ cut]
Hi Ara,

The problem with this approach is that you have to know the uri of the
server at fork time, which can be a problem.
(And I didn’t realize that you can destroy a DRbObject with
DRb.stop_service – I associate that with the server side of things).

I’ve found a mote generic way of forking a process that is a DRb
client, but this has as drawback that it has an initimate knowlegde of
the way DRb is implemented.
module DRb
class DRbConn
def self.fork
f = @mutex.synchronize do
Process::fork
end
if f.nil?
@pool = []
if block_given?
yield
exit 0
end
end
f
end
end
end

This just deletes the commection pool in the child.
Forking within the mutex is because it’s possible to fork with some
other thread having the mutex locked, which causes problems for the
one-thread child.

But it’s a bit of a kludge and I would prefer it if DRbObject had a
method to deal with it.

Thanks,

Han H.