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

On Tue, Mar 11, 2003 at 08:48:09AM +0900, Hal E. Fulton wrote:
> > I would really like the 'extend ThreadSafe' to perform the initialisation
> of
> > the instance variable automatically though. Is there a way to do that?
> 
> I suppose you could add a line to
> __send__:
> 
> if not @__tsmutex then @__tsmutex  utex.new end

  or shorter:    @__tsmutex || utex.new

> But then that slows down every method call by a
> few more microseconds.

More importantly, it's not thread-safe for the creation of the Mutex itself.
I guess it's unlikely that this race would cause a problem, but in theory it
could.

I like Kent's suggestion of using the extend_object trigger at the point
where the Module is included, this wasn't something I was aware of.

In the end, because I needed a pattern which distributed requests amongst
multiple objects, I came up with the attached solution. It lets you create a
pool of objects, and you can pass the object representing the pool to a DRb
server (or a SOAP server, or any server which runs concurrent threads on the
same object). Incoming method calls are parcelled out to the individual
objects, and no object handles more than one request at a time.

I'd be interested in any comments on the attached solution (especially flaws
that I've overlooked); if it is sound then I'll stick it up on the Wiki.

Regards,

Brian.

--M9NhX3UHpAaciwkO
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="facade.rb"

in

 ynopsis
   require 'facade'

   def Foo
     def initialize
       @ok  rue
     end
     def doit(x)
       puts "Doing #{x}, I am #{id}"
       sleep 2
       puts "Done #{x} by #{id}"
     end
     def suicide; @ok  alse; end
     def alive?;  @ok; end
   end

   pool  acade::SimplePool.new(5, :doit, :suicide) { Foo.new }
   DRb.start_service('druby://0.0.0.0:9000', pool)

 escription

Lets you set up a thread-safe pool of objects. Method calls to the pool are
delegated to one of its objects, and only one method is allowed to run on
each object at any time.

If the object responds to an 'alive?' message this is used to check whether
it is still usable. If it returns nil or false, then a fresh object is
created to replace it in the pool.

 lass Methods
--- Facade::SimplePool.new( count, methods... ) { factoryblock }
    Constructs a pool of 'count' objects, calling the factoryblock to create
    each one.

    If necessary, you can list the methods which you wish to make available
    (this is necessary for DRb, which refuses calls to methods which are
    not explicitly included in the object)

ίΕ

require 'thread'

module Facade
  class SimplePool
    def initialize(n, *methodlist, &factory)
      @n  
      @factory  actory
      @mutex  utex.new
      @poolnotempty  onditionVariable.new
      @pool  1..n).collect { @factory.call }
      if methodlist
        methodlist.each do |m|
          eval "class <<self; def #{m}(*a); method_missing(:#{m},*a); end; end"
        end
      end
    end

    def method_missing(meth, *argary)
      item  c  il
      argary  argary] unless argary.kind_of? Array
      @mutex.synchronize do
        @poolnotempty.wait(@mutex) if @pool.size 0
        item  pool.shift
      end
      if item.respond_to?(:alive?) && !item.alive?
        item  factory.call
      end
      begin
        rc  tem.__send__(meth, *argary)
      ensure
        @mutex.synchronize do
          @pool.push item
          @poolnotempty.signal
        end
      end
      rc
    end

    def alive?   # don't proxy this to a random object in the pool
      true
    end
  end
end

--M9NhX3UHpAaciwkO--