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

scala.concurrent.stm.Ref.scala Maven / Gradle / Ivy

The newest version!
/* scala-stm - (c) 2009-2010, Stanford University, PPL */

package scala.concurrent.stm

import impl.{RefFactory, STMImpl}
import reflect.{AnyValManifest, OptManifest}

/** `object Ref` contains factory methods that allocate an STM-managed memory
 *  location and return a `Ref` instance that provides access to that location.
 *
 *  @author Nathan Bronson
 */
object Ref extends RefCompanion {

  protected def factory: RefFactory = STMImpl.instance

  /** `Ref.View` provides access to the contents of a `Ref` without requiring
   *  that an implicit `InTxn` be available.  When called from within the
   *  dynamic scope of a transaction, `View`'s methods operate as part of that
   *  transaction.  When there is no transaction active `View`'s methods are
   *  still atomic, but only for the duration of the method call.
   *
   *  A mental model of `View` is that `view.foo(args)` acts like
   *  `atomic { implicit t => view.ref.foo(args) }`.
   */
  trait View[A] extends Source.View[A] with Sink.View[A] {

    /** Returns a `Ref` that accesses the same memory location as this view.
     *  The returned `Ref` might be the original reference that was used to
     *  construct this view, or it might be a `Ref` that is equivalent (and
     *  `==`) to the original.
     *  @return a `Ref` that accesses the same memory location as this view.
     */
    override def ref: Ref[A]

    /** Works like `set(v)`, but returns the old value.  This is an
     *  atomic swap, equivalent to atomically performing a `get`
     *  followed by `set(v)`.
     *  @return the previous value held by `ref`.
     */
    def swap(v: A): A

    /** Equivalent to atomically executing
     *  `(if (before == get) { set(after); true } else false)`, but may be more
     *  efficient, especially if there is no enclosing atomic block.
     *  @param before a value to compare against the `ref`'s contents using the
     *      value's `==` method.
     *  @param after a value to store if `before` was equal to the previous
     *      contents.
     *  @return true if `before` was equal to the previous value of the viewed
     *      `Ref`, false otherwise.
     */
    def compareAndSet(before: A, after: A): Boolean

    /** Equivalent to atomically executing
     *  `(if (before eq get) { set(after); true } else false)`, but may be more
     *  efficient, especially if there is no enclosing atomic block.
     *  @param before a value to compare against the `ref`'s contents using
     *      reference identity equality (`eq`).
     *  @param after a value to store if `before` was `eq` to the previous
     *      contents.
     *  @return true if `before` was `eq` to the previous value of the viewed
     *      `Ref`, false otherwise.
     */
    def compareAndSetIdentity[B <: A with AnyRef](before: B, after: A): Boolean

    /** Atomically replaces the value ''v'' stored in the `Ref` with
     *  `f`(''v'').  Some `Ref` implementations may defer execution of `f` or
     *  call `f` multiple times to avoid transaction conflicts.
     *  @param f a function that is safe to call multiple times, and safe to
     *      call later during the enclosing atomic block, if any.
     */
    def transform(f: A => A)

    /** Atomically replaces the value ''v'' stored in the `Ref` with
     *  `f`(''v''), returning the old value.  `transform` should be preferred
     *  if the return value is not needed, since it gives the STM more
     *  flexibility to avoid transaction conflicts.
     *  @param f a function that is safe to call multiple times, and safe to
     *      call later during any enclosing atomic block.
     *  @return the previous value of the viewed `Ref`.
     */
    def getAndTransform(f: A => A): A

    /** Atomically replaces the value ''v'' stored in the `Ref` with
     *  `f`(''v''), returning the new value.  `transform` should be preferred
     *  if the return value is not needed, since it gives the STM more
     *  flexibility to avoid transaction conflicts.
     *  @param f a function that is safe to call multiple times, and safe to
     *      call later during any enclosing atomic block.
     *  @return the new value of the viewed `Ref`.
     */
    def transformAndGet(f: A => A): A

    /** Atomically replaces the value ''v'' stored in the `Ref` with
     *  `pf`(''v'') if `pf.isDefinedAt`(''v''), returning true, otherwise
     *  leaves the element unchanged and returns false.  `pf.apply` and
     *  `pf.isDefinedAt` might be invoked multiple times by the STM, and might
     *  be called later in any enclosing atomic block.
     *  @param pf a partial function that is safe to call multiple times, and
     *      safe to call later during any enclosing atomic block.
     *  @return `pf.isDefinedAt``(''v''), where ''v'' is the element held by
     *      this `Ref` on entry.
     */
    def transformIfDefined(pf: PartialFunction[A,A]): Boolean

    /** Transforms the value stored in the `Ref` by incrementing it.
     *
     *  '''Note: Implementations may choose to ignore the provided `Numeric[A]`
     *  instance if `A` is a primitive type.'''
     *
     *  @param rhs the quantity by which to increment the value of `ref`.
     */
    def += (rhs: A)(implicit num: Numeric[A]) { transform { v => num.plus(v, rhs) } }

    /** Transforms the value stored in the `Ref` by decrementing it.
     *
     *  '''Note: Implementations may choose to ignore the provided `Numeric[A]`
     *  instance if `A` is a primitive type.'''
     *
     *  @param rhs the quantity by which to decrement the value of `ref`.
     */
    def -= (rhs: A)(implicit num: Numeric[A]) { transform { v => num.minus(v, rhs) } }

    /** Transforms the value stored in the `Ref` by multiplying it.
     *
     *  '''Note: Implementations may choose to ignore the provided `Numeric[A]`
     *  instance if `A` is a primitive type.'''
     *
     *  @param rhs the quantity by which to multiple the value of `ref`.
     */
    def *= (rhs: A)(implicit num: Numeric[A]) { transform { v => num.times(v, rhs) } }

    /** Transforms the value stored in `ref` by performing a division on it,
     *  throwing away the remainder if division is not exact for instances of
     *  type `A`.  The careful reader will note that division is actually
     *  provided by `Fractional[A]` or `Integral[A]`, it is not defined on
     *  `Numeric[A]`.  To avoid compile-time ambiguity this method accepts a
     *  `Numeric[A]` and assumes that it can be converted at runtime into
     *  either a `Fractional[A]` or an `Integral[A]`.
     *
     *  '''Note: Implementations may choose to ignore the provided `Numeric[A]`
     *  instance if `A` is a primitive type.'''
     *
     *  @param rhs the quantity by which to divide the value of `ref`.
     */
    def /= (rhs: A)(implicit num: Numeric[A]) {
      num match {
        //case numF: Fractional[A] => transform { v => numF.div(v, rhs) }
        case numF: Fractional[_] => transform { v => numF.asInstanceOf[Fractional[A]].div(v, rhs) }
        //case numI: Integral[A] => transform { v => numI.quot(v, rhs) }
        case numI: Integral[_] => transform { v => numI.asInstanceOf[Integral[A]].quot(v, rhs) }
      }
    }

    // If you implement a Ref.View proxy, you should define a hashCode and
    // equals that delegate to the underlying Ref or Ref.View.  Ref and
    // Ref.View that refer to the same memory location should be equal.
    //
    // override def hashCode: Int = underlying.hashCode
    // override def equals(rhs: Any): Boolean = underlying == rhs
  }
}

// All of object Ref's functionality is actually in RefCompanion.  The split
// allows RefCompanion to be tested independently of the globally configured
// RefFactory, without introducing an extra level of mutable indirection for
// normal uses of the companion object.

trait RefCompanion {
  
  protected def factory: RefFactory

  /** Returns a `Ref` instance that manages a newly allocated memory location
   *  holding values of type `A`.  If you have an initial value `v0` available,
   *  `Ref(v0)` should be preferred.
   */
  def make[A]()(implicit om: OptManifest[A]): Ref[A] = (om match {
    case m: ClassManifest[_] => m.newArray(0).asInstanceOf[AnyRef] match {
      // these can be reordered, so long as Unit comes before AnyRef
      case _: Array[Boolean] => apply(false)
      case _: Array[Byte]    => apply(0 : Byte)
      case _: Array[Short]   => apply(0 : Short)
      case _: Array[Char]    => apply(0 : Char)
      case _: Array[Int]     => apply(0 : Int)
      case _: Array[Float]   => apply(0 : Float)
      case _: Array[Long]    => apply(0 : Long)
      case _: Array[Double]  => apply(0 : Double)
      case _: Array[Unit]    => apply(())
      case _: Array[AnyRef]  => factory.newRef(null.asInstanceOf[A])(m.asInstanceOf[ClassManifest[A]])
    }
    case _ => factory.newRef(null.asInstanceOf[Any])(implicitly[ClassManifest[Any]])
  }).asInstanceOf[Ref[A]]

  /** Returns a `Ref` instance that manages a newly allocated memory location,
   *  initializing it to hold `initialValue`.  The returned `Ref` is not part
   *  of any transaction's read or write set.
   *
   *  Example: {{{
   *    val x = Ref("initial") // creates a Ref[String]
   *    val list1 = Ref(Nil : List[String]) // creates a Ref[List[String]]
   *    val list2 = Ref[List[String]](Nil)  // creates a Ref[List[String]]
   *  }}}
   */
  def apply[A](initialValue: A)(implicit om: OptManifest[A]): Ref[A] = om match {
    case m: AnyValManifest[_] => newPrimitiveRef(initialValue, m)
    case m: ClassManifest[_] => factory.newRef(initialValue)(m.asInstanceOf[ClassManifest[A]])
    case _ => factory.newRef[Any](initialValue).asInstanceOf[Ref[A]]
  }

  private def newPrimitiveRef[A](initialValue: A, m: AnyValManifest[_]): Ref[A] = {
    (m.newArray(0).asInstanceOf[AnyRef] match {
      case _: Array[Int]     => apply(initialValue.asInstanceOf[Int])
      case _: Array[Boolean] => apply(initialValue.asInstanceOf[Boolean])
      case _: Array[Byte]    => apply(initialValue.asInstanceOf[Byte])
      case _: Array[Short]   => apply(initialValue.asInstanceOf[Short])
      case _: Array[Char]    => apply(initialValue.asInstanceOf[Char])
      case _: Array[Float]   => apply(initialValue.asInstanceOf[Float])
      case _: Array[Long]    => apply(initialValue.asInstanceOf[Long])
      case _: Array[Double]  => apply(initialValue.asInstanceOf[Double])
      case _: Array[Unit]    => apply(initialValue.asInstanceOf[Unit])
    }).asInstanceOf[Ref[A]]
  }

  def apply(initialValue: Boolean): Ref[Boolean] = factory.newRef(initialValue)
  def apply(initialValue: Byte   ): Ref[Byte]    = factory.newRef(initialValue)
  def apply(initialValue: Short  ): Ref[Short]   = factory.newRef(initialValue)
  def apply(initialValue: Char   ): Ref[Char]    = factory.newRef(initialValue)
  def apply(initialValue: Int    ): Ref[Int]     = factory.newRef(initialValue)
  def apply(initialValue: Long   ): Ref[Long]    = factory.newRef(initialValue)
  def apply(initialValue: Float  ): Ref[Float]   = factory.newRef(initialValue)
  def apply(initialValue: Double ): Ref[Double]  = factory.newRef(initialValue)
  def apply(initialValue: Unit   ): Ref[Unit]    = factory.newRef(initialValue)
}

/** Provides access to a single element of type ''A''.  Accesses are
 *  performed as part of a ''memory transaction'' that comprises all of the
 *  operations of an atomic block and any nested blocks.  Single-operation
 *  memory transactions may be performed without an explicit atomic block using
 *  the `Ref.View` returned from `single`.  The software transactional memory
 *  performs concurrency control to make sure that all committed transactions
 *  are linearizable.  Reads and writes performed by a successful transaction
 *  return the same values as if they were executed instantaneously at the
 *  transaction's commit (linearization) point.
 *
 *  The static scope of an atomic block is defined by access to an implicit
 *  `InTxn` passed to the block by the STM.  Atomic blocks nest, so to
 *  participate in an atomic block for which a `InTxn` is not conveniently
 *  available, just create a new atomic block using {{{
 *    atomic { implicit t =>
 *      // the body
 *    }
 *  }}}
 *  In the static scope of an atomic block reads and writes of a `Ref`
 *  are performed by `x.get` and `x.set(v)`, or more concisely by `x()` and
 *  `x() = v`. `x.single` returns a `Ref.View` that will dynamically resolve
 *  the current scope during each method call, automatically creating a
 *  single-operation atomic block if no transaction is active.
 *
 *  It is possible for separate `Ref` instances to refer to the same element;
 *  in this case they will compare equal.  (As an example, a transactional
 *  array class might store elements in an array and create `Ref`s on demand.)
 *  `Ref`s may be provided for computed values, such as the emptiness of a
 *  queue, to allow  conditional retry and waiting on semantic properties.
 *
 *  To perform an access outside a transaction, use the view returned by
 *  `single`.  Each access through the returned view will act as if it was
 *  performed in its own single-operation transaction, dynamically nesting into
 *  an active atomic block as appropriate.
 *
 *  `Ref`'s companion object contains factory methods that create `Ref`
 *  instances paired with a single STM-managed memory location.
 *
 *  @author Nathan Bronson
 */
trait Ref[A] extends RefLike[A, InTxn] with Source[A] with Sink[A] {

  /** Returns a `Ref.View` that allows access to the contents of this `Ref`
   *  without requiring that a `InTxn` be available.  Each operation on the view
   *  will act as if it is performed in its own "single-operation" atomic
   *  block, nesting into an existing transaction if one is active.
   *
   *  A mental model of this method is that `ref.single.foo(args)` acts like
   *  `atomic { implicit t => ref.foo(args) }`.
   */
  override def single: Ref.View[A]

  // If you implement a Ref proxy, you should define a hashCode and
  // equals that delegate to the underlying Ref or Ref.View.  Ref and
  // Ref.View that refer to the same memory location should be equal.
  //
  // override def hashCode: Int = underlying.hashCode
  // override def equals(rhs: Any): Boolean = underlying == rhs
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy