Atomic Operations in Ruby

Often, when it comes to parallel programming in Ruby, people make the mistake of assuming that compound assignment operators like += are atomic — that is, that the value will be read, modified, and written again without interference by other activity.

This is never guaranteed in JRuby, and it isn’t even normally guaranteed by other Ruby implementations: modulo some small details, in Ruby @foo += 1 is not fundamentally different from @foo = @foo + 1 — a lot can happen while dispatching that :+ method.

The only safe/portable way to do an atomic update in any Ruby up until this point has been to use a Mutex:

@mutex.synchronize { @foo += 1 }

This avoids the “lost update” problem, among others.

In a Ruby implementation with a weaker memory model (JRuby and IronRuby, currently), when multiple threads are accessing a variable, this is also important to do in order to have a memory barrier that prevents CPU instruction reordering or cache issues. Indeed, if you have multiple threads using @foo, then you would need to use the mutex even for simple accesses (since Ruby has no language provision for Java-style volatile fields):

return @mutex.synchronize { @foo }
@mutex.synchronize { @foo = 3 }

This works (and performs better than you might expect), but for very simple things it invites needless problems with lock contention. It’d be nice to have portable volatile variables with support for lockfree atomic update operations, wouldn’t it?

In response to this need, Charles Nutter (headius) and I came up with the atomic gem, which provides a simple class, Atomic, which embodies a volatile reference and supports atomic updates to it.

It’s pretty primitive so far (and the fallback portable/generic implementation still uses locks), but it provides the essentials in a lockfree fashion under JRuby:

  • Atomic.new(initial_value=nil) – creates a new atomic reference
  • Atomic#value – returns the value
  • Atomic#value=(new_value) – sets the value
  • Atomic#swap(new_value) – sets the value and returns the old one
  • Atomic#update { |old_value| ... } – atomically sets the value to the result of the block
  • Atomic#try_update { |old_value| ... } – same thing, but fails rather than retrying on conflict

So, rather than this (which won’t actually work reliably in multiple threads under load):

@foo = 0
# ...
@foo += 1

You can do this:

@foo = Atomic.new(0)
# ...
@foo.update { |v| v + 1 }

The block passed to update doesn’t run exclusively; instead, the reference’s value at the beginning and end of the block are compared to determine whether it is safe to update (under JRuby, this uses a hardware test-and-set instruction when available). If there was a change, then Atomic#update tries the block again with the externally-changed value.

In other words, Atomic#update proceeds optimistically and retries the block if there is a conflict. Because long-running expressions increase the likelihood of conflict, the expression in the block should be simple, and because it can sometimes be retried multiple times, it should ideally have no side-effects.

Atomic#update is lockfree, but some specific use cases would benefit strongly from actual hardware-level atomic operations, particularly atomic increment and decrement. Since they cannot be performed on general Ruby types, support for these operations will require a separate API (perhaps a special subclass which holds only Fixnum values), but we’re certainly interested in supporting such things in the long run.

Check out the atomic gem on github.