Last active
December 31, 2015 22:59
-
-
Save rosenfeld/8056742 to your computer and use it in GitHub Desktop.
Isn't MRI 2.0 behaving wrongly here?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MRI 2.0.0p353: | |
$ ruby test.rb | |
1 | |
1 | |
102 | |
102 | |
JRuby 1.7.9: | |
$ ruby test.rb | |
19 | |
119 | |
120 | |
120 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'thread' | |
class A | |
attr_reader :a | |
@@lock = Mutex.new | |
@@incrementer = 0 | |
def a | |
sleep 1 | |
@a ||= @@lock.synchronize{@@incrementer += 1} | |
end | |
def incrementer | |
@@incrementer | |
end | |
end | |
a = A.new | |
100.times.map do | |
Thread.start{a.a} | |
end.each &:join | |
100.times.map do | |
Thread.start{A.new.a} | |
end.each &:join | |
p a.a, a.incrementer, A.new.a, a.incrementer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@rosenfeld Now the problem is that the RHS of the ||= is not substantial enough for threads to context switch.
MRI does not run threads concurrently. Each thread gets a slice of time to execute before it gets put back into the queue and another thread gets a chance to run. In this case, the #a method's logic runs easily within that timeslice.
If we replace the #a method with one that forces a thread context switch, MRI fails to do the assigns atomically just like JRuby:
This gives me a "bad" result immediately:
$ rvm ruby-2.1 do ruby blah.rb
2
102
103
103
This may seem artificial, but if the RHS had a bit more work to do, like calculating an expensive value or making a database connection, it would be very easy for MRI to schedule another thread while that code is still running.
Trust me...there's absolutely nothing guaranteeing that ||= runs atomically on MRI.