From: Sam Dalton on
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

def initialize(capacity)
@capacity = capacity
@front = 0
@back = 0
@elements = Array.new(capacity)
@empty_cond = new_cond
@full_cond = new_cond
super()
end

def get
@empty_cond.wait_while {empty?}
element = nil
synchronize do
element = @elements[@front]
@elements[@front] = nil
@front = (@front + 1) % @capacity
@full_cond.signal
end
return element
end

def put(element)
@full_cond.wait_while {full?}
synchronize do
@elements[@back] = element
@back = (@back + 1) % @capacity
@empty_cond.signal
end
end

def full?
result = false
synchronize do
result = (@front == @back and @elements[@front] != nil)
end
return result
end

def empty?
result = false
synchronize do
result = (@front == @back and @elements[@front] == nil)
end
return result
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'

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 != Thread.current'.

I am using the buffer for a simple producer/consumer web server, like so

buffer = SynchronisedBuffer.new(10)
workers = []

for i in (1..10)
workers[i] = Worker.new(buffer)
end

while socket = server.accept
buffer.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.

Sam Dalton
--
Posted via http://www.ruby-forum.com/.

From: Robert Klemme on
2010/7/1 Sam Dalton <mail(a)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
>
>   def initialize(capacity)
>      @capacity = capacity
>      @front = 0
>      @back = 0
>      @elements = Array.new(capacity)
>      @empty_cond = new_cond
>      @full_cond = new_cond
>      super()
>   end
>
>   def get
>       @empty_cond.wait_while {empty?}

The line above must be moved into the synchronized block.

>       element = nil

No need for this.

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

Just move the next line into the block.

>        return element
>   end
>
>   def put(element)
>       @full_cond.wait_while {full?}

The line above must be moved down 1 line.

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

This is sufficient:

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

>   end
>
>   def empty?

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

>       result = false
>       synchronize do
>           result = (@front == @back and @elements[@front] == nil)
>        end
>        return result
>   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 != Thread.current'.
>
> I am using the buffer for a simple producer/consumer web server, like so
>
> buffer = SynchronisedBuffer.new(10)
> workers = []
>
> for i in (1..10)
>   workers[i] = Worker.new(buffer)
> end
>
> while socket = server.accept
>    buffer.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


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

From: Sam Dalton on
Hi Robert,

Thanks for the help, it works perfectly now. I see that I should treat
the synchronised block as wrapping the entire method body, just as the
synchronise keyword would in Java. Makes sense :)

Is it possible to subscribe to this forum via RSS (or another method)?
Looking forward to the day when I've improved enough to help others.

Regards,

Sam
--
Posted via http://www.ruby-forum.com/.

From: Robert Klemme on
2010/7/1 Sam Dalton <mail(a)samdalton.co.nz>:
> Thanks for the help, it works perfectly now. I see that I should treat
> the synchronised block as wrapping the entire method body, just as the
> synchronise keyword would in Java. Makes sense :)

Not necessarily. The synchronized block must be only as long as
necessary to guard the critical section. I'd rather say, it's bad to
always synchronize complete methods.

> Is it possible to subscribe to this forum via RSS (or another method)?

There is news:comp.lang.ruby and ruby-talk mailing list - all are
mirrored full duplex (see [1]).

> Looking forward to the day when I've improved enough to help others.

The day will come - inevitably. :-)

Kind regards

robert


[1] http://www.ruby-lang.org/en/community/mailing-lists/


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