Project

slave

0.01
No commit activity in last 3 years
No release in over 3 years
easily start a drb server in another process
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme
SYNOPSIS

  the Slave class forks a process and starts a drb server in the child using
  any object as the server.  the process is detached so it is not required
  (nor possible) to wait on the child pid.  a Heartbeat is set up between the
  parent and child processes so that the child will exit of the parent exits
  for any reason - preventing orphaned slaves from running indefinitely.  the
  purpose of Slaves is to be able to easily set up a collection of objects
  communicating via drb protocols instead of having to use IPC. 

  typical usage:

    slave = Slave::new{ AnyObject.new }

    slave.object                  #=> handle on drb object
    slave.uri                     #=> uri of the drb object
    slave.socket                  #=> unix domain socket path for drb object
    slave.psname                  #=> title shown in ps/top

    object = slave.object

    value = object.any_method     #=> use the object normally 

  slaves may be configured via the environment, the Slave class, or via the
  ctor for object itself.  attributes which may be configured include

    * object : specify the slave object.  otherwise block value is used.
    * socket_creation_attempts : specify how many attempts to create a unix domain socket will be made 
    * debug : turn on some logging to STDERR
    * psname : specify the name that will appear in 'top' ($0)
    * at_exit : specify a lambda to be called in the *parent* when the child dies
    * dumped : specify that the slave object should *not* be DRbUndumped (default is DRbUndumped) 
    * threadsafe : wrap the slave object with ThreadSafe to implement gross thread safety 

URIS

  http://rubyforge.org/projects/codeforpeople/
  http://codeforpeople.com/lib/ruby/slave

HISTORY
  1.3.0:
    - fixes for 1.9.2 (undef object_id)
    - fixes for osx (too long socket names) 

  1.2.1:
    - jruby/ThreadSafe patches from skaar and ez.  using slave.rb with jruby,
      how is that!?

  1.2.0:
    - cleaned up a bunch of warnings.  thanks eric kolve <ekolve@gmail.com>
      for reporting them.

  1.1.0:
    - replaced HeartBeat class with LifeLine.

    - __HUGE__ cleanup of file descriptor/fork management with tons of help
      from skaar and ezra.  thanks guys!

    - introduced Slave.object method used to return any object directory from
      a child process.  see samples/g.rb.

    - indroduced keyword to automatically make slave objects threadsafe.
      remember that your slave object must be threadsafe because they are
      being server via DRb!!!


  1.0.0:
    - THIS RELEASE IS !! NOT !! BACKWARD COMPATIBLE.  NOTE NEW CTOR SYNTAX.

    - detach method also sets up at_exit handler.  extra protection from
      zombies.

    - ezra zygmuntowicz asked for a feature whereby a parent could be notified
      when a child exited.  obviously such a mechanism should be both async
      and sync.  to accomplish this the wait method was extended to support a
      callback with is either sync or async

        slave = Server.new{ Server.new }

        slave.wait and puts 'this is sync!'

        slave.wait(:non_block=>true){ 'this is async!' }
   
    - patch to getval from skaar<skaar@waste.org>.  the impl dropped opts
      delgating to the class method from the instance one.

  0.2.0:
    incorporated joel vanderWerf's patch such that, if no object is passed the
    block is used to create one ONLY in the child.  this avoids having a copy
    in both parent and child is that needs to be avoided due to, for instance,
    resource consumption.


  0.0.1:
    - patch from Logan Capaldo adds block form to slave new, block is run in the
      child

    - added a few more samples/*

    - added Slave#wait

    - added status information to slaves

    - added close-on-exec flag to pipes in parent process

  0.0.0:
    - initial version

SAMPLES


  <========< samples/a.rb >========>

  ~ > cat samples/a.rb

    require 'slave'
     
    # simple usage is simply to stand up a server object as a slave.  you do not
    # need to wait for the server, join it, etc.  it will die when the parent
    # process dies - even under 'kill -9' conditions
    #
      class Server
        def add_two(n)
          n + 2
        end
      end
    
      slave = Slave.new :object => Server.new
    
      server = slave.object
      p server.add_two(40) #=> 42
    
      slave.shutdown

  ~ > ruby samples/a.rb

    42


  <========< samples/b.rb >========>

  ~ > cat samples/b.rb

    require 'slave'
    #
    # if certain operations need to take place in the child only a block can be
    # used
    #
      class Server
        def connect_to_db 
          "we only want to do this in the child process!"
          @connection = :postgresql
        end
        attr :connection
      end
    
      slave = Slave.new('object' => Server.new){|s| s.connect_to_db}
    
      server = slave.object
    
      p server.connection  #=> :postgresql 
    #
    # errors in the child are detected and raised in the parent
    #
      slave = Slave.new('object' => Server.new){|s| s.typo} #=> raises an error!

  ~ > ruby samples/b.rb

    :postgresql
    samples/b.rb:22: undefined method `typo' for #<Server:0x10030aa60> (NoMethodError)
    	from ./lib/slave.rb:367:in `[]'
    	from ./lib/slave.rb:367:in `initialize'
    	from samples/b.rb:22:in `new'
    	from samples/b.rb:22


  <========< samples/c.rb >========>

  ~ > cat samples/c.rb

    require 'slave'
    #
    # if no slave object is given the block itself is used to contruct it 
    #
      class Server
        def initialize
          "this is run only in the child"
          @pid = Process.pid
        end
        attr 'pid'
      end
    
      slave = Slave.new{ Server.new }
      server = slave.object
    
      p Process.pid
      p server.pid # not going to be the same as parents!
    #
    # errors are still detected though
    #
      slave = Slave.new{ fubar } # raises error in parent

  ~ > ruby samples/c.rb

    48871
    48872
    samples/c.rb:21: undefined local variable or method `fubar' for main:Object (NameError)
    	from ./lib/slave.rb:359:in `call'
    	from ./lib/slave.rb:359:in `initialize'
    	from samples/c.rb:21:in `new'
    	from samples/c.rb:21


  <========< samples/d.rb >========>

  ~ > cat samples/d.rb

    require 'slave'
    #
    # at_exit hanlders are handled correctly in both child and parent 
    #
      at_exit{ p 'parent' }
      slave = Slave.new{ at_exit{ p 'child' };  'the server is this string' }
    #
    # this will print 'child', then 'parent'
    #

  ~ > ruby samples/d.rb

    "child"
    "parent"


  <========< samples/e.rb >========>

  ~ > cat samples/e.rb

    require 'slave'
    #
    # slaves never outlive their parent.  if the parent exits, even under kill -9,
    # the child will die.
    #
      slave = Slave.new{ at_exit{ p 'child' };  'the server is this string' }
    
      Process.kill brutal=9, the_parent_pid=Process.pid
    #
    # even though parent dies a nasty death the child will still print 'child'
    #

  ~ > ruby samples/e.rb

    "child"


  <========< samples/f.rb >========>

  ~ > cat samples/f.rb

    require 'slave'
    #
    # slaves created previously are visible to newly created slaves - in this
    # example the child process of slave_a communicates directly with the child
    # process of slave_a 
    #
      slave_a = Slave.new{ Array.new }
      slave_b = Slave.new{ slave_a.object }
    
      a, b = slave_b.object, slave_a.object
    
      b << 42
      puts a #=> 42

  ~ > ruby samples/f.rb

    42


  <========< samples/g.rb >========>

  ~ > cat samples/g.rb

    require 'slave'
    #
    # Slave.object can used when you want to construct an object in another
    # process.  in otherwords you want to fork a process and retrieve a single
    # returned object from that process as opposed to setting up a server.
    #
      this = Process.pid
      that = Slave.object{ Process.pid }
    
      p 'this' => this, 'that' => that
    
    #
    # any object can be returned and it can be returned asychronously via a thread
    #
      thread = Slave.object(:async => true){ sleep 2 and [ Process.pid, Time.now ] }
      this = [ Process.pid, Time.now ]
      that = thread.value 
      
      p 'this' => this, 'that' => that

  ~ > ruby samples/g.rb

    {"that"=>48890, "this"=>48889}
    {"that"=>[48891, Mon Oct 10 08:21:47 -0600 2011], "this"=>[48889, Mon Oct 10 08:21:45 -0600 2011]}