Synchronized Circular Buffer

S

Sam Dalton

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 = 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
 
R

Robert Klemme

2010/7/1 Sam Dalton said:
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 =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/
 
S

Sam Dalton

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
 
R

Robert Klemme

2010/7/1 Sam Dalton said:
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 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/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top