All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.util.Var.scala Maven / Gradle / Ivy

package com.twitter.util

import java.util.{List => JList}
import scala.collection.{Seq => AnySeq}
import scala.collection.compat.immutable.ArraySeq
import scala.collection.immutable.SortedSet
import scala.collection.mutable.Buffer
import scala.jdk.CollectionConverters._
import scala.language.higherKinds

/**
 * Vars are values that vary over time. To create one, you must give it an
 * initial value.
 *
 * {{{
 * val a = Var[Int](1)
 * }}}
 *
 * A Var created this way can be sampled to retrieve its current value,
 *
 * {{{
 * println(Var.sample(a)) // prints 1
 * }}}
 *
 * or, invoked to assign it new values.
 *
 * {{{
 * a.update(2)
 * println(Var.sample(a)) // prints 2
 * }}}
 *
 * Vars can be derived from other Vars.
 *
 * {{{
 * val b = a.flatMap { x => Var(x + 2) }
 * println(Var.sample(b)) // prints 4
 * }}}
 *
 * And, if the underlying is assigned a new value, the derived Var is updated.
 * Updates are computed lazily, so while assignment is cheap, sampling is where
 * we pay the cost of the computation required to derive the new Var.
 *
 * {{{
 * a.update(1)
 * println(Var.sample(b)) // prints 3
 * }}}
 *
 * A key difference between the derived Var and its underlying is that derived
 * Vars can't be assigned new values. That's why `b`, from the example above,
 * can't be invoked to assign it a new value, it can only be sampled.
 *
 * @note Vars do not always perform the minimum amount of
 * re-computation.
 * @note There are no well-defined error semantics for Var. Vars are
 * computed lazily, and the updating thread will receive any
 * exceptions thrown while computing derived Vars.
 *
 * Note: There is a Java-friendly API for this trait: [[com.twitter.util.AbstractVar]].
 */
trait Var[+T] { self =>
  import Var.Observer

  /**
   * Observe this Var. `f` is invoked each time the variable changes,
   * and synchronously with the first call to this method.
   */
  private[util] final def observe(f: T => Unit): Closable = observe(0, Observer(f))

  /**
   * Concrete implementations of Var implement observe. This is
   * called for each toplevel observe. Depths indicate the relative
   * structural depth of the observation, from the frame of reference
   * of the root call to observe. (Each Var derived via flatMap
   * increases the depth.) Depths are used to order the invocation of
   * update callbacks. This is used to ensure that updates proceed in
   * topological order so that every input variable is fully resolved
   * before recomputing a derived variable.
   */
  protected def observe(depth: Int, obs: Observer[T]): Closable

  /**
   * Create a derived variable by applying `f` to the contained
   * value.
   */
  def map[U](f: T => U): Var[U] = flatMap(t => Var.value(f(t)))

  /**
   * Create a dependent Var which behaves as `f` applied to the
   * current value of this Var. FlatMap manages a dynamic dependency
   * graph: the dependent Var is detached and recomputed  whenever
   * the outer Var changes, but only if there are any observers.  An
   * unobserved Var returned by flatMap will not invoke `f`
   */
  def flatMap[U](f: T => Var[U]): Var[U] = new Var[U] {
    private class ObserverManager(depth: Int, obs: Observer[U]) extends Closable with (T => Unit) {
      private[this] var closable: Closable = Closable.nop
      private[this] var closed: Boolean = false

      def apply(t: T): Unit = {
        // We have to synchronize and make sure we're not already closed or else
        // we may generate a new observation that has raced with the a `close`
        // call and lost, therefore making an orphan and a resource leak.
        val toClose = this.synchronized {
          if (closed) Closable.nop
          else {
            val old = closable
            closable = f(t).observe(depth + 1, obs)
            old
          }
        }
        // TODO: Right now we rely on synchronous propagation; and
        // thus also synchronous closes. We should instead perform
        // asynchronous propagation so that it is is safe &
        // predictable to have asynchronously closing Vars, for
        // example. Currently the only source of potentially
        // asynchronous closing is Var.async; here we have modified
        // the external process to close asynchronously with the Var
        // itself. Thus we know the code path here is synchronous:
        // we control all Var implementations, and also all Closable
        // combinators have been modified to evaluate their respective
        // Futures eagerly.
        val done = toClose.close()
        assert(done.isDefined)
      }

      def close(deadline: Time): Future[Unit] = {
        val toClose = this.synchronized {
          closed = true
          val old = closable
          closable = Closable.nop
          old
        }
        toClose.close(deadline)
      }
    }

    def observe(depth: Int, obs: Observer[U]): Closable = {
      val manager = new ObserverManager(depth, obs)
      val outer = self.observe(depth, Observer(manager))
      Closable.sequence(outer, manager)
    }
  }

  def join[U](other: Var[U]): Var[(T, U)] =
    for { t <- self; u <- other } yield (t, u)

  /**
   * An Event where changes in Var are emitted. The current value
   * of this Var is emitted synchronously upon subscription.
   *
   * All changes to this Var are guaranteed to be published to the
   * Event.
   */
  lazy val changes: Event[T] = new Event[T] {
    def register(s: Witness[T]) =
      self.observe { newv => s.notify(newv) }
  }

  /**
   * Produce an [[Event]] reflecting the differences between
   * each update to this [[Var]].
   */
  def diff[CC[_]: Diffable, U](implicit toCC: T <:< CC[U]): Event[Diff[CC, U]] =
    changes.diff

  def sample(): T = Var.sample(this)
}

/**
 * Abstract `Var` class for Java compatibility.
 */
abstract class AbstractVar[T] extends Var[T]

/**
 * Note: There is a Java-friendly API for this object: [[com.twitter.util.Vars]].
 */
object Var {

  /**
   * A Var observer. Observers are owned by exactly one producer,
   * enforced by a leasing mechanism.
   */
  final class Observer[-T](observe: T => Unit) {
    private[this] var thisOwner: AnyRef = null
    private[this] var thisVersion = Long.MinValue

    /**
     * Claim this observer with owner `newOwner`. Claiming
     * an observer gives the owner exclusive rights to publish
     * to it while it has not been claimed by another owner.
     */
    def claim(newOwner: AnyRef): Unit = synchronized {
      if (thisOwner ne newOwner) {
        thisOwner = newOwner
        thisVersion = Long.MinValue
      }
    }

    /**
     * Publish the given versioned value with the given owner.
     * If the owner is not current (because another has claimed
     * the observer), or if the version has already published (by
     * assumption of a monotonically increasing version number)
     * the publish operation is a no-op.
     */
    def publish(owner: AnyRef, value: T, version: Long): Unit = synchronized {
      if ((owner eq thisOwner) && thisVersion < version) {
        thisVersion = version
        observe(value)
      }
    }
  }

  object Observer {
    def apply[T](k: T => Unit): Observer[T] = new Observer(k)
  }

  /**
   * Sample the current value of this Var. Note that this may lead to
   * surprising results for lazily defined Vars: the act of observing
   * a Var may be kick off a process to populate it; the value
   * returned from sample may then reflect an intermediate value.
   */
  def sample[T](v: Var[T]): T = {
    var opt: Option[T] = None
    v.observe(0, Observer(v => opt = Some(v))).close()
    opt.get
  }

  object Sampled {
    def apply[T](v: T): Var[T] = value(v)
    def unapply[T](v: Var[T]): Option[T] = Some(sample(v))
  }

  /**
   * Create a new, updatable Var with an initial value. We call
   * such Vars independent -- derived Vars being dependent
   * on these.
   */
  def apply[T](init: T): Var[T] with Updatable[T] with Extractable[T] =
    new UpdatableVar(init)

  /**
   * Constructs a [[Var]] from an initial value plus an event stream of
   * changes. Note that this eagerly subscribes to the event stream;
   * it is unsubscribed whenever the returned [[Var]] is collected.
   */
  def apply[T](init: T, e: Event[T]): Var[T] = {
    val v = Var(init)

    // In order to support unsubscribing from e when v is no longer referenced
    // we must avoid e keeping a strong reference to v.
    val witness = Witness.weakReference(v)
    Closable.closeOnCollect(e.register(witness), v)

    v
  }

  /**
   * Patch reconstructs a [[Var]] based on observing the incremental
   * changes presented in the underlying [[Diff Diffs]].
   *
   * Note that this eagerly subscribes to the event stream;
   * it is unsubscribed whenever the returned [[Var]] is collected.
   */
  def patch[CC[_]: Diffable, T](diffs: Event[Diff[CC, T]]): Var[CC[T]] = {
    val v = Var(Diffable.empty[CC, T]: CC[T])

    // In order to support unsubscribing from diffs when v is no longer referenced
    // we must avoid diffs keeping a strong reference to v.
    val witness = Witness.weakReference { (diff: Diff[CC, T]) =>
      synchronized {
        v.update(diff.patch(v()))
      }
    }
    val closable = diffs.register(witness)
    Closable.closeOnCollect(closable, v)

    v
  }

  /**
   * Create a new, constant, v-valued Var.
   */
  def value[T](v: T): Var[T] with Extractable[T] = new ConstVar(v)

  /**
   * Collect a collection of Vars into a Var of collection.
   * Var.collect can result in a stack overflow if called with a large sequence.
   * Var.collectIndependent breaks composition with respect to update propagation.
   * That is, collectIndependent can fail to correctly update interdependent vars,
   * but is safe for independent vars.
   *
   * {{{
   *  // Example of difference between collect and collectIndependent:
   *  val v1 = Var(1)
   *  val v2 = v1.map(_*2)
   *  val vCollect = Var.collect(Seq(v1, v2)).map { case Seq(a, b) => (a, b) }
   *  val vCollectIndependent = Var.collectIndependent(Seq(v1, v2)).map { case Seq(a, b) => (a, b) }
   *  val refCollect = new AtomicReference[Seq[(Int, Int)]]
   *  vCollect.changes.build.register(Witness(refCollect))
   *  val refCollectIndependent = new AtomicReference[Seq[(Int, Int)]]
   *  vCollectIndependent.changes.build.register(Witness(refCollectIndependent))
   *  v1() = 2
   *  // refCollect == Vector((1,2), (2,4))
   *  // refCollectIndependent == Vector((1,2), (2,2), (2,4))
   * }}}
   */
  def collect[T](vars: AnySeq[Var[T]]): Var[Seq[T]] = {
    val vs = vars.toIndexedSeq

    def tree(begin: Int, end: Int): Var[Seq[T]] =
      if (begin == end) Var(Seq.empty)
      else if (begin == end - 1) vs(begin).map(t => Seq(t))
      else {
        val n = (end - begin) / 2

        for {
          left <- tree(begin, begin + n)
          right <- tree(begin + n, end)
        } yield left ++ right
      }

    tree(0, vs.length)
  }

  /**
   * Collect a collection of Vars into a Var of collection.
   * Var.collectIndependent breaks composition with respect to update propagation.
   * That is, collectIndependent can fail to correctly update interdependent vars,
   * but is safe for independent vars.
   *
   * {{{
   *  // Example of difference between collect and collectIndependent:
   *  val v1 = Var(1)
   *  val v2 = v1.map(_*2)
   *  val vCollect = Var.collect(Seq(v1, v2)).map { case Seq(a, b) => (a, b) }
   *  val vCollectIndependent = Var.collectIndependent(Seq(v1, v2)).map { case Seq(a, b) => (a, b) }
   *  val refCollect = new AtomicReference[Seq[(Int, Int)]]
   *  vCollect.changes.build.register(Witness(refCollect))
   *  val refCollectIndependent = new AtomicReference[Seq[(Int, Int)]]
   *  vCollectIndependent.changes.build.register(Witness(refCollectIndependent))
   *  v1() = 2
   *  // refCollect == Vector((1,2), (2,4))
   *  // refCollectIndependent == Vector((1,2), (2,2), (2,4))
   * }}}
   */
  def collectIndependent[T](vars: AnySeq[Var[T]]): Var[Seq[T]] =
    async(Seq.empty[T]) { u =>
      val N = vars.size

      // `filling` represents whether or not we have gone through our collection
      // of `Var`s and added observations. Once we have "filled" the `cur` array
      // for the first time, we can publish an initial update to `u`. Subsequent,
      // updates to each constintuent Var are guarded by a lock on the `cur` and
      // we ensure that we publish the update before any new updates can come
      // through.
      //
      // @note there is still a subtle race where we can be in the middle
      // of "filling" and receive updates on previously filled Vars. However, this
      // is an acceptable race since technically we haven't added observations to all
      // of the constituent Vars yet.
      //
      // @note "filling" only works with the guarantee that the initial `observe` is
      // synchronous. This should be the case with Vars since they have an initial value.
      var filling = true
      val cur = new Array[Any](N)

      def publishAt(i: Int): T => Unit = { newValue =>
        cur.synchronized {
          cur(i) = newValue
          if (filling && i == N - 1) filling = false
          if (!filling) {
            // toSeq does not do a deep copy until 2.13
            val copy = new Array[Any](N)
            Array.copy(cur, 0, copy, 0, cur.length)
            u() = ArraySeq.unsafeWrapArray(copy).asInstanceOf[Seq[T]]
          }
        }
      }

      val closables = new Array[Closable](N)
      var i = 0
      val iter = vars.iterator
      while (iter.hasNext) {
        val v = iter.next()
        closables(i) = v.observe(publishAt(i))
        i += 1
      }

      Closable.all(ArraySeq.unsafeWrapArray(closables): _*)
    }

  /**
   * Collect a List of Vars into a new Var of List.
   *
   * @param vars a java.util.List of Vars
   * @return a Var[java.util.List[A]] containing the collected values from vars.
   */
  def collect[T <: Object](vars: JList[Var[T]]): Var[JList[T]] = {
    // we cast to Object and back because we need a ClassTag[T]
    val list = vars.asScala.asInstanceOf[Buffer[Var[Object]]]
    collect(list).map(_.asJava).asInstanceOf[Var[JList[T]]]
  }

  private object create {
    sealed trait State[+T]
    object Idle extends State[Nothing]
    case class Observing[T](n: Int, v: Var[T], c: Closable) extends State[T]
  }

  /**
   * Create a new Var whose values are provided asynchronously by
   * `update`. The returned Var is dormant until it is observed:
   * `update` is called by-need. Such observations are also reference
   * counted so that simultaneous observations do not result in
   * multiple invocations of `update`. When the last observer stops
   * observing, the [[com.twitter.util.Closable]] returned
   * from `update` is closed. Subsequent observations result in a new
   * call to `update`.
   *
   * `empty` is used to fill the returned Var until `update` has
   * provided a value. The first observation of the returned Var is
   * synchronous with the call to `update`--it is guaranteed the the
   * opportunity to fill the Var before the observer sees any value
   * at all.
   *
   * Updates from `update` are ignored after the returned
   * [[com.twitter.util.Closable]] is closed.
   */
  def async[T](empty: T)(update: Updatable[T] => Closable): Var[T] = new Var[T] { self =>
    import create._
    private var state: State[T] = Idle

    private val closable = Closable.make { deadline =>
      self.synchronized {
        state match {
          case Idle =>
            Future.Done
          case Observing(1, _, c) =>
            state = Idle
            // We close the external process asynchronously from the
            // async Var so that it is safe to Await Var.close() in
            // flatMap. (See the TODO there.)
            c.close(deadline)
            Future.Done
          case Observing(n, v, c) =>
            state = Observing(n - 1, v, c)
            Future.Done
        }
      }
    }

    protected def observe(depth: Int, obs: Observer[T]): Closable = {
      val v = self.synchronized {
        state match {
          case Idle =>
            val v = Var(empty)
            val c = update(v)
            state = Observing(1, v, c)
            v
          case Observing(n, v, c) =>
            state = Observing(n + 1, v, c)
            v
        }
      }

      val c = v.observe(depth, obs)
      Closable.sequence(c, closable)
    }
  }
}

private object UpdatableVar {
  import Var.Observer

  private final class Party[T](val observer: Observer[T], val depth: Int, val sequence: Long) {
    @volatile var active: Boolean = true
  }

  private val partyOrder: Ordering[Party[_]] = new Ordering[Party[_]] {
    def compare(a: Party[_], b: Party[_]): Int = {
      val c1 = java.lang.Integer.compare(a.depth, b.depth)
      if (c1 != 0) return c1
      java.lang.Long.compare(a.sequence, b.sequence)
    }
  }
}

private[util] class UpdatableVar[T](init: T) extends Var[T] with Updatable[T] with Extractable[T] {
  import UpdatableVar._
  import Var.Observer

  // The state must be mutated or read under a synchronized block on 'this'. The value can be read
  // w/o an external synchronization.
  @volatile private[this] var value = init
  private[this] var version = 0L
  private[this] var partySequence = 0L
  @volatile private[this] var parties =
    SortedSet.empty[Party[T]](partyOrder.asInstanceOf[Ordering[Party[T]]])

  def apply(): T = value

  def update(newValue: T): Unit = {
    val v = synchronized {
      version += 1
      value = newValue
      version
    }

    // Another party maybe be racing to register while we're updating with the new value.
    // This is not a big deal b/c observers prevent double-updates by design (via versions).
    parties.foreach { p =>
      // An antecedent update may have closed the current
      // party (e.g. flatMap does this); we need to check that
      // the party is active here in order to prevent stale updates.
      if (p.active) p.observer.publish(this, newValue, v)
    }
  }

  protected def observe(depth: Int, obs: Observer[T]): Closable = {
    obs.claim(this)

    val (p, curValue, curVersion) = synchronized {
      val party = new Party(obs, depth, partySequence)
      parties = parties + party
      partySequence += 1
      (party, value, version)
    }

    obs.publish(this, curValue, curVersion)

    new Closable {
      def close(deadline: Time): Future[Unit] = {
        p.active = false
        UpdatableVar.this.synchronized {
          parties = parties - p
        }
        Future.Done
      }
    }
  }

  override def toString: String = "Var(" + value + ")@" + hashCode
}

/**
 * A constant [[Extractable]] [[Var]] on `v`.
 */
class ConstVar[T](v: T) extends Var[T] with Extractable[T] {
  protected def observe(depth: Int, obs: Var.Observer[T]): Closable = {
    obs.claim(this)
    obs.publish(this, v, 0)
    Closable.nop
  }

  def apply(): T = v
}

/**
 * Java adaptation of `Var[T] with Updatable[T] with Extractable[T]`.
 */
class ReadWriteVar[T](init: T) extends UpdatableVar[T](init)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy