From: MenTaLguY Date: 2007-06-12T03:45:36+09:00 Subject: Re: Synchronized attr_accessor On Tue, 12 Jun 2007 03:13:39 +0900, "Nasir Khan" wrote: > @@__ms_c_lock.synchronize { @__ms_lock = Mutex.new unless > @__ms_lock > } unless @__ms_lock > > What is the race condition...? It's known as the "double-checked locking" antipattern. Generally, anything like: unless @obj @lock.synchronize do unless @obj @obj = Whatever.new end end end @obj.some_method is wrong. This is because (when dealing with real multi-threading -- not a major issue in Ruby yet, but it soon will be!) we depend on locks not only for preserving the order of operations, but ensuring that each thread has a consistent view of memory. For instance, when reading the instance variable @obj outside the protection of the lock, it is possible that a thread could @obj being non-null, but not seeing the effects of initializing the object until much later! If you're interested in learning more about the details of the problem, read here: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html The article deals with Java, but in practice the problems apply to pretty much any contemporary language with native threads (and certainly to JRuby!). Anwyay, the correct approach is something like this: obj = @lock.synchronize { @obj ||= Whatever.new } obj.some_method Or even the slightly more verbose: obj = @lock.synchronize do @obj = Whatever.new unless @obj @obj end obj.some_method Note that all assignments to @obj and all reads of @obj happen within the protection of the lock. > And why did you change it to have two identical unless's one after the > other in the second snippet? Yes, the two unlesses are indeed redundant in that case, and failed to fix the problem generally if there were reads of the instance variable outside the lock. -mental