From: MenTaLguY Date: 2007-07-14T08:59:24+09:00 Subject: Re: Which Hash methods are thread safe? On Sat, 14 Jul 2007 06:54:00 +0900, Mike Kasick 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