8

The fine Twitter util library has the following Java class, which is extended by a Scala class that reads the volatile field and updates it using AtomicReferenceFieldUpdater. Access must be at least private, i.e., must allow other.state.

Is the motivational claim in the comment true? How true? ("How" means in what way and to what degree.)

public class IVarField<A> {
  /** This is needed because we cannot create a field like this in Scala.  */
  volatile State<A> state;
}
4

1 回答 1

14

Something like the following works.

Making the field "private [this]" turns it into the desired field reference ("getfield").

Access from a different instance is through an accessor "state()" which, if made @inline, will happily loosen the access restriction to the field.

That also means that the updater (which happens to reside in the companion module) can also access it.

(Because the normal accessor for the var is not emitted for an object-private member, you can define your own, using parens. But you don't need to use parens at the call site, other.state. Your uniform access principle dollars at work. Or Swiss francs.)

Also notice the totally hip usage of `new` in backticks for a param name. I don't even know how to make the ticks show up in this markup. Because both params are the same type, one is likely to want to write cas(expect=prev, `new`=changed), so I might have used next instead, but if this markup supported a hipster thumbs-up, I'd give it right now. :+1: Did it work? Can anyone see my thumb? [I guess I saw that on github. Hubster, not hipster.]

object IVar {
  // TODO: retrieve mangled name by reflection
  private final val stateField = "com$twitter$concurrent$IVar$$state"
  private val stateUpd = AtomicReferenceFieldUpdater.newUpdater(
        classOf[IVar[_]], classOf[State[_]], stateField)
}

final class IVar[A] {  //}extends IVarField[A] {
  import IVar._

  @volatile private[this] var state: State[A] = initState: State[A]
      
  @inline private final def state(): State[A] = this.state
          
  override def toString = "Ivar@%s(state=%s)".format(hashCode, state)

  @inline private[this]
  def cas(expect: State[A], `new`: State[A]) = stateUpd.compareAndSet(this, expect, `new`)

  def depth: Int = {
    @tailrec
    def loop(iv: IVar[_], d: Int): Int = iv.state match {
      case Linked(iv) => loop(iv, d + 1)
      case _ => d
    }
    loop(this, 0)
  }
  // etc
}

Showing that state() is really inlined:

private final int loop$1(com.twitter.concurrent.IVar, int);
  flags: ACC_PRIVATE, ACC_FINAL
  Code:
    stack=3, locals=5, args_size=3
       0: aload_1
  // Field   com$twitter$concurrent$IVar$$state:Lcom/twitter/concurrent/ivar/State;
       1: getfield      #16                 
       4: astore_3

Not only is it "OK to ask and answer your own questions",

https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

it's infinitely more satisfying.

(More power to Daniel Sobral, Rex Kerr, Retronym and the rest of the Justice League.)

于 2012-09-19T00:32:54.430 回答