When I run jruby-lint on my (Rails) app, I get several of these:
Non-local operator assignment is not guaranteed to be atomic
Which point to code that looks like this:
def foo
@foo ||= Foo.new
end
Or this:
config.assets.precompile += %w( email/email.css )
Some of which is in app/, some of which is in config/. I'm guessing that this warning is only relevant to the cases where the thing on the left is an Array, and to fix it I should use Threadsafe::Array
?
Which types of these things do I need to change?
In jruby compound expressions like ||=
are not atomically executed. When you write:
foo ||= 'bar'
What is actually executed internally is something like:
1. unless foo
2. foo = 'bar'
3. end
Because line 1 and line 2 are evaluated separately it is possible in a multi-threaded app that the state could be changed by a different thread in between these two, something like:
thread 1: foo ||= 'bar'
thread 2: foo ||= 'baz'
Which executes like:
# foo has not been set yet
1. thread 1: unless foo
2. thread 2: unless foo
3. thread 1: foo = 'bar'
4. thread 2: foo = 'baz'
# ...
Note that foo will end up getting re-assigned to 'baz' by the second thread even though it already has a value. Using +=
is equally problematic because this:
thread 1: x += 1
thread 2: x += 1
Will be executed like this:
# x starting value of 0
1. thread1: tempvar = x + 1 # 1
2. thread2: tempvar = x + 1 # 1
3. thread1: x = tempvar # 1
4. thread2: x = tempvar # 1
So x
should be 2 after the two operations but is in fact only incremented once.
If you are running a single-threaded app/script in jruby none of this is an issue. If you are going to be running with multiple threads then these operations are not safe to use within the executing environment of those threads if they are used on variables which are accessed by more than one thread.
In environments where thread safety matters you can fix this by wrapping the operation in a mutex or using a primitive which ensures thread safety for the specific operation.
Also the atomic gem can be used with jRuby to ensure atomicity of check & update operations. I'm not sure if it supports arrays though.
More information about managing concurrency in jRuby.
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With