--n8g4imXOkfNTN/H1
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Fri, Mar 21, 2003 at 02:41:23AM +0900, Chris Pine wrote:
> In DRb (or drb, or druby, or... what should I call it?), I'm starting a
> server and my Apache/mod_ruby scripts are connecting to it when they need
> to.
> 
> My question:  If two (or more) people connect to my webserver at roughly the
> same time, on two different Apache/mod_ruby processes, are their requests
> all sent to the same DRb object, one at a time?

They are sent to the same DRb object, and may be processed at the *same*
time. DRb starts a new thread for handling each incoming request.

You have to serialise the requests yourself, or build your own pool of
objects if that's how you want them handled. A sample is attached below, if
it's any use.

Cheers,

Brian.

--n8g4imXOkfNTN/H1
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="simplepool.rb"

in

 ynopsis
   require 'pool'

   class Foo
     def doit(x)
       puts "Doing #{x}, I am #{id}"
       sleep 2
       puts "Done #{x} by #{id}"
     end
   end

   pool  ool::SimplePool.new(5) { Foo.new }
   obj  ool::Facade.new(pool, :doit)

   require 'drb'
   DRb.start_service('druby://0.0.0.0:9001', obj)

 escription

A SimplePool is a thread-safe pool of objects. You can 'get' an item
from the pool, and it is not available again until it has been 'put'
back in (or the block associated with 'get' has been run)

A Facade is an object which relays method calls to an arbitrary object
in the pool.

 lass Methods
--- SimplePool.new( start [max, check, exception] ) { factoryblock }
    Constructs a pool of objects, calling the factoryblock to create
    each one. Whenever the pool is empty it may create additional objects,
    up to the limit of 'max' objects active at any one time. Use maxl
    for unlimited instances.

 nstance Methods
--- SimplePool#get { block }
    Retrieve an item from the pool, execute the associated block passing
    the item as a parameter, and then put it back into the pool.

    When the pool is created, it can be given two extra parameters:
    'check' is a method name, and 'exception' is an exception class
    (defaulting to StandardError)

    If the execution of the block raises an error, then the object's
    'check' method is called. If it returns false/nil, or raises an
    exception, then the object is considered dead and is not returned
    to the pool. The original exception is still returned to the caller.

    Example:

      class Foo
        initialize
          @db  BI::connect('dbi:mysql:whatever')
        end
        alive?
          @db.select_one("select 4+5")[0] 9    # connection sanity check
        end
      end
      pool  ool::SimplePool(5, 5, :alive?) { Foo.new }

    You can force objects to be destroyed after every exception by giving
    'true' as the method name (or any non-symbol), e.g.

      pool  ool::SimplePool(1, 10, true, Exception)

--- SimplePool#get        returns obj
--- SimplePool#put( obj )
    Methods to remove an item from the pool, and return it to the pool.
    put nil if you want to permanently discard it rather than return it
    (this allows a subsequent 'get' to create a new instance)

 lass Methods
--- Facade.new( pool, [methodnames] )
    Constructs a new facade object for the given pool.
    If methnames are given, then explicit methods are added to the facade
    for those methods. This is necessary for DRb where method_missing
    does not work.

 nstance Methods
--- Facade#<somemethod>( <args> )
    Pick an item from the pool and send it the given method/args, after
    which it is returned to the pool.

ίΕ


require 'thread'

module Pool

class SimplePool
  def initialize(initial maxitial, checkmethl, errandardError, &factory)
    @max  ax
    @checkmeth  heckmeth
    @err  rr
    @factory  actory
    @poolaccess  utex.new
    @poolnotempty  onditionVariable.new
    @createobj  utex.new
    @pool  1..initial).collect { @factory.call }
    @numobjs  nitial
  end

  def get
    item  il
    @poolaccess.synchronize do
      if @pool.size > 0
        item  pool.shift
      elsif !@max || @numobjs < @max
        @numobjs + 
        # we will create it outside of this mutex
      else
        @poolnotempty.wait(@poolaccess)
        item  pool.shift
      end
    end

    # Don't create object under the poolaccess mutex, as that would block
    # out other fetch/put operations. But do serialise it under a separate
    # mutex, as it may not be thread-safe (e.g. if it uses class variables)
    unless item
      @createobj.synchronize do
        item  factory.call
      end
    end

    return item unless block_given?

    begin
      result  ield item
    rescue @err boom
      if @checkmeth
        begin
          item  il unless item.send(@checkmeth)
        rescue Exception
          item  il
        end
      end
      raise boom
    ensure
      put item
    end
    result
  end

  def put(item)
    @poolaccess.synchronize do
      if item
        @pool.push item
        @poolnotempty.signal
      else
        @numobjs - 
      end
    end
    nil
  end

  def totalsize
    @numobjs    # approx (changes asynchronously)
  end

  def freesize
    @pool.size  # approx (changes asynchronously)
  end
end

class Facade
  def initialize(pool, *methodlist)
    @pool  ool
    methodlist.each do |m|
      eval "class <<self; def #{m}(*a,&b); method_missing(:#{m},*a,&b); end; end"
    end
  end

  def method_missing(meth, *argary, &block)
    @pool.get do |obj|
       obj.__send__(meth, *argary, &block)
    end
  end
end

end # module Pool

--n8g4imXOkfNTN/H1--