2010/7/1 Sam Dalton <mail / samdalton.co.nz>:
> I'm relatively new to Ruby, having switched from a PHP and Java
> background, and I'm having some trouble with the Monitor class trying to
> implement a synchronised circular buffer. The other posts here on
> synchronised methods aren't quite relevant enough unfortunately.
>
> My current implementation is the following
>
> require 'monitor'
>
> class SynchronisedBuffer < Monitor
>
> =A0 def initialize(capacity)
> =A0 =A0 =A0@capacity =3D capacity
> =A0 =A0 =A0@front =3D 0
> =A0 =A0 =A0@back =3D 0
> =A0 =A0 =A0@elements =3D Array.new(capacity)
> =A0 =A0 =A0@empty_cond =3D new_cond
> =A0 =A0 =A0@full_cond =3D new_cond
> =A0 =A0 =A0super()
> =A0 end
>
> =A0 def get
> =A0 =A0 =A0 @empty_cond.wait_while {empty?}

The line above must be moved into the synchronized block.

> =A0 =A0 =A0 element =3D nil

No need for this.

> =A0 =A0 =A0 synchronize do
> =A0 =A0 =A0 =A0 =A0 element =3D @elements[@front]
> =A0 =A0 =A0 =A0 =A0 @elements[@front] =3D nil
> =A0 =A0 =A0 =A0 =A0 @front =3D (@front + 1) % @capacity
> =A0 =A0 =A0 =A0 =A0 @full_cond.signal
> =A0 =A0 =A0 =A0end

Just move the next line into the block.

> =A0 =A0 =A0 =A0return element
> =A0 end
>
> =A0 def put(element)
> =A0 =A0 =A0 @full_cond.wait_while {full?}

The line above must be moved down 1 line.

> =A0 =A0 =A0 synchronize do
> =A0 =A0 =A0 =A0 =A0 @elements[@back] =3D element
> =A0 =A0 =A0 =A0 =A0 @back =3D (@back + 1) % @capacity
> =A0 =A0 =A0 =A0 =A0 @empty_cond.signal
> =A0 =A0 =A0 =A0end
> =A0 end
>
> =A0 def full?
> =A0 =A0 =A0 result =3D false
> =A0 =A0 =A0 synchronize do
> =A0 =A0 =A0 =A0 =A0 result =3D (@front =3D=3D @back and @elements[@front]=
 !=3D nil)
> =A0 =A0 =A0 =A0end
> =A0 =A0 =A0 =A0return result

This is sufficient:

def full?
  synchronize do
    (@front =3D=3D @back and @elements[@front] !=3D nil)
  end
end

> =A0 end
>
> =A0 def empty?

Same as above - saves you some typing. :-)

> =A0 =A0 =A0 result =3D false
> =A0 =A0 =A0 synchronize do
> =A0 =A0 =A0 =A0 =A0 result =3D (@front =3D=3D @back and @elements[@front]=
 =3D=3D nil)
> =A0 =A0 =A0 =A0end
> =A0 =A0 =A0 =A0return result
> =A0 end
>
> end
>
> This has been adapted from a version I had written in Java. The problem
> is that when I have a thread call 'get' on the buffer, I receive a
> ThreadException saying 'current thread not owner'

You need to be holding the lock when waiting on a condition variable
(same story in Java btw).

> This is being thrown by mon_check_owner, and a bit of poking around
> shows that it fails because mon_owner is set to nil in the condition
> '@mon_owner !=3D Thread.current'.
>
> I am using the buffer for a simple producer/consumer web server, like so
>
> buffer =3D SynchronisedBuffer.new(10)
> workers =3D []
>
> for i in (1..10)
> =A0 workers[i] =3D Worker.new(buffer)
> end
>
> while socket =3D server.accept
> =A0 =A0buffer.put(socket)
> end
>
> I'm writing this purely to gain a better understanding of some important
> ruby classes like thread, monitor and socket, and am aware that there
> are other ruby web servers I could use straight away :). I'm also trying
> to understand the 'ruby way' of coding, so any comments as to how the
> above code might better be written would be really appreciated.

That's usually a good thing to do.  I do it myself all the time.
Nothing beats one's own experience.

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/