MenTaLguY
7/13/2007 11:59:00 PM
On Sat, 14 Jul 2007 06:54:00 +0900, Mike Kasick <mkasick-rt@club.cc.cmu.edu> wrote:
> On Sat, Jul 14, 2007 at 05:53:37AM +0900, MenTaLguY wrote:
> It's just the first time I've worked on a project
> where I didn't feel comfortable with requiring so much synchronization
> for reading, when in practice, rarely will data actually be mutated.
Resist those feelings until they're backed up with real measurements;
you'll be happier and make less work for yourself.
> The one situation I'm working on right now is basically a pub/sub
> mechanism on top of drb. Basically a bunch of clients register a
> callback object in a hash (keyed by some client identifier) on a server.
> When a message is published, it gets sent to each client registered in
> the hash.
> The problem with synchronization is that only one message can be sent at
> a time.
It also establishes an ordering between delivery and (un)subscription, which
might be important. On the other hand, if your clients are robust against
unexpected messages, you can certainly take advantage of extra concurrency
to get better throughput.
> I could make a copy of the hash value array so that drb calls
> don't have to made while synchronized, but that feels like I'd have to
> make a lot of unnecessary copies.
> Basically the point is that messages are published often, subscriptions
> rarely change.
Why not make the copies at subscription time rather than delivery time?
def initialize
@lock = Mutex.new
@subscriptions = Hash.new { |h,k| h[k] = [].freeze }
end
def publish(publisher, message)
@lock.synchronize do
@subscriptions[publisher]
end.each do |subscriber|
subscriber.deliver message
end
self
end
def subscribe(subscriber, publisher)
@lock.synchronize do
subscriptions = @subscriptions[publisher].dup
subscriptions.push subscriber
@subscriptions[publisher] = subscriptions.freeze
end
self
end
def unsubscribe(subscriber, publisher)
@lock.synchronize do
subscriptions = @subscriptions[publisher].dup
subscriptions.delete subscriber
@subscriptions[publisher] = subscriptions.freeze
end
self
end
The freezing isn't really necessary, but it makes it a little
clearer what's happening (and catches you if you mess up).
> If there was a good r/w lock implementation with writer's priority,
> that would solve the problem rather easily.
Possibly, yes. You'd want to measure, though, especially as a Ruby
implementation of an R/W lock would typically introduce a lot of
extra method calls (which can be very expensive)
> I guess I could also use Thread.critical= when making subscription
> updates, but isn't that deprecated?
I wouldn't recommend using it.
-mental