5
votes

If I have a linked node in some collection structure I don't really want its next link to be an AtomicReference (I need atomic CAS update) so I declare it as:

@volatile var next: Node[A] = _n

and then in the companion declare:

val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[Link[_]], classOf[Node[_]], "n")
def cas[A](target: Link[A], old: Node[A], newNode: Node[A]) = updater.compareAndSet(target, old, newNode);

At runtime I get the following error:

java.lang.RuntimeException: java.lang.IllegalAccessException: 
  Class concurrent.Link$ can not access a member of class concurrent.Link
  with modifiers "private volatile"
    at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.<init>(AtomicReferenceFieldUpdater.java:189)
    at java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdater.java:65)
    at concurrent.Link$.<init>(Link.scala:106)
    ...

So, at runtime the companion object is concurrent.Link$ not concurrent.Link and a different class cannot access the private member of another.

BUT, if I javap -p concurrent.Link

I get:

Compiled from "Link.scala"
public final class concurrent.Link implements concurrent.Node,scala.ScalaObject,scala.Product,java.io.Serializable{
private final java.lang.Object value;
private volatile com.atlassian.util.scala.concurrent.Node node;
public static final boolean cas(com.atlassian.util.scala.concurrent.Link, com.atlassian.util.scala.concurrent.Node, com.atlassian.util.scala.concurrent.Node);
public static final java.util.concurrent.atomic.AtomicReferenceFieldUpdater updater();

So, I have everything but the static instance of the AtomicReferenceFieldUpdater declared on my Link class.

The question is, how do I get an instance of AtomicReferenceFieldUpdater in Scala that points to a volatile var?

The only way I've found so far is to go back to Java (implement an AbstractLink with the next Node field and a static AtomicReferenceFieldUpdater) and inherit from that, which is ugly.

2

2 Answers

4
votes

Hard. Since Scala makes fields private, and only accessor methods available, this might not be possible.

When doing this, I eventually decided to do it by creating Java base class with the volatile field and updating through there.

Java file:

public class Base {
   volatile Object field = null;
}

Scala file:

class Cls extends Base {
  val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[Base[_]], classOf[Object[_]], "field")
  def cas[A](old: Object, n: Object) = updater.compareAndSet(this, old, n);
}

I haven't come up with a different approach.

0
votes

I've been told there is no way to do this in pure scala, and you need to define a class in Java to do this. Fortunately cross-compilation is pretty simple, but still, annoying!