On Tue, 12 Jun 2007 05:42:02 +0900, "Nasir Khan" <rubylearner / gmail.com> wrote:
> While the comparison is against Java synchronization, does the
> Mutex#synchronize has the same behavior as synchronized in Java?

Roughly speaking, yes.

> I see the code under 1.8  and synchronize basically does -
> 
> # File lib/thread.rb, line 132
>   def synchronize
>     lock
>     begin
>       yield
>     ensure
>       unlock
>     end
>   end
> 
> The keyword "synchronized" in Java implies not just locking but perhaps
> more importantly "synchronization" of the current thread's working memory with
> heap. [JLS 17.4]

i.e. memory barriers.

It's probably better to compare Mutex#lock and Mutex#unlock with the JVM monitorenter and monitorexit instructions, since Java's synchronized translates to a single pair of monitorenter and monitorexit instructions on the JVM.

In JRuby, a call to each of #lock and #unlock involves both monitorenter and monitorexit, so Mutex#synchronize is actually slightly more aggressive than Java synchronized in terms of its memory effects.  However, the portable assumption is probably still only that it is no less aggressive than the JVM.

> I can understand the behavior of JRuby but how would the other Ruby
> interpreters be implemented for such things?

In practice, implementors defer to whatever the underlying platform is; under those circumstances, the only option for programmers is to adopt a very conservative posture towards locking and memory model.

> If one were to implement a Ruby interpreter today where should he/she
> start in the absence of a language spec? Take MRI as the spec? But MRI may
> not be addressing all the issues like the one in question here.

Indeed -- it doesn't.  Though I suppose one could reasonably say that MRI, with its green threads, effectively fences all reads and writes, but that's obviously a bit heavy-handed to actually implement.

>> Yes, but even the volatile keyword doesn't do what you expect.  Despite
>> widespread folklore, volatile by itself is _not_ sufficient to ensure
>> thread safety -- you need to use operations which include memory barriers
>> (generally by using the provided synchronization primitives).
> 
> BTW the folklore is codified as JLS v3 and is available since Java 5. (JSR
> 133 fixed in 2004)

That was badly needed (and I believe a similar fix was made in the CLR 2.0 memory model as well), but JRuby still targets Java 4, as does nearly all of other Java code I've been asked to work on professionally -- there's a bit of a gap between a JSR going in, and that Java version being widely adopted.

(Admittedly, I may be behind the curve Java 5 adoption, and I'm less sure about CLR 2.0 adoption as a whole.)

Anyway, for right now, this is a moot point in Ruby, since double-checked locking isn't possible to implement safely there; we have neither volatile (broken or otherwise), nor anything like .NET's System.Threading.Thread.MemoryBarrier().  The only way to get the required memory barriers in Ruby is to trust the library locking primitives to provide them when they are needed.  The safe thing is to simply never to perform accesses to shared variables outside the protection of a lock, since that being safe is the minimum guarantee any locking implementation must provide.

-mental