Generic CAS (Compare-And-Set) For Scala

There are many ways to update a value atomically, so without introducing racing conditions in multithreaded environments. A well-performing way of doing this in Java is to use an AtomicReference or one of its companion classes in the package java.util.concurrent.atomic. However, using an AtomicReference is quite cumbersome. But with Scala, you can easily factor out the cumbersome usage pattern like follows.

For comparison, let’s first see how to atomically update a String value with an AtomicReference in Java 7:

AtomicReference<String> ref = new AtomicReference<>("Hello world!");
...
String update;
while (true) {
  String expect = ref.get();
  update = expect.toUpperCase();
  if (ref.compareAndSet(expect, update))
    break;
}

upon return from the while-loop, update should refer to "HELLO WORLD!" unless ref was changed meanwhile. This is fine if you only have a single reference and a single algorithm to update the referenced value. However, if you happen to need multiple references or multiple update algorithms for their referenced value, then repeating the while-loop may be cumbersome and error-prone.

Could we do better? Sure! Only the reference and the update algorithm vary from usage to usage, so the repeated pattern is the while-loop. We could easily encapsulate this code block in a method, but with Java 7 we would still have to define an interface to encapsulate the update algorithm and what’s even more important, we would have to write an anonymous inner class every time we want to use it! After all, this would save us nothing.

With Scala however, thanks to its closures we can easily do so without hassle. First, let’s encapsulate the while-loop in a function:

def atomic[V](ref: AtomicReference[V])(next: V => V): V = {
  while (true) {
    val expect = ref.get
    val update = next(expect)
    if (ref.compareAndSet(expect, update))
      return update
  }
  throw new AssertionError
}

This function requires a reference parameter and a function parameter which updates the value of the reference. Here’s how the first example translates to use the atomic-function:

val ref = new AtomicReference("Hello world!")
...
atomic(ref)(_.toUpperCase)

Now you can easily apply different update algorithms to the same reference or even vice versa. Here is another example:

atomic(ref)(_.reverse)

Conclusion

Scala’s support for closures allows us to encapsulate repeated code blocks while still being able to easily call the encapsulated code blocks. This particular example showed us how to encapsulate the cumbersome while-loop which is required to atomically update an AtomicReference.