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

zio.stm.TRef.scala Maven / Gradle / Ivy

There is a newer version: 2.1.16
Show newest version
/*
 * Copyright 2019-2024 John A. De Goes and the ZIO Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package zio.stm

import zio._
import zio.stacktracer.TracingImplicits.disableAutoTrace
import zio.stm.ZSTM.internal._

import java.util.concurrent.atomic.{AtomicLong, AtomicReference}

/**
 * A `TRef` is a purely functional description of a mutable reference that can
 * be modified as part of a transactional effect. The fundamental operations of
 * a `TRef` are `set` and `get`. `set` transactionally sets the reference to a
 * new value. `get` gets the current value of the reference.
 *
 * NOTE: While `TRef` provides the transactional equivalent of a mutable
 * reference, the value inside the `TRef` should be immutable. For performance
 * reasons `TRef` is implemented in terms of compare and swap operations rather
 * than synchronization. These operations are not safe for mutable values that
 * do not support concurrent access.
 */
final class TRef[A] private (
  private[stm] val versioned: AtomicReference[A],
  @volatile private[stm] var todo: Map[TxnId, Todo] = Map.empty
) extends Serializable { self =>

  @deprecated("kept for binary compatibility only", "2.1.8")
  def this(versioned: Versioned[A], todo: AtomicReference[Map[TxnId, Todo]]) =
    this(new AtomicReference(versioned.value), todo.get())

  private[stm] val lock: ZSTMLockSupport.Lock = ZSTMLockSupport.Lock(true)

  private val id = TRef.idCounter.incrementAndGet()

  private[this] val hc = super.hashCode()

  override def hashCode(): Int = hc

  /**
   * Retrieves the value of the `TRef`.
   */
  def get: USTM[A] =
    ZSTM.Effect((journal, _, _) => getOrMakeEntry(journal).unsafeGet[A])

  /**
   * Sets the value of the `TRef` and returns the old value.
   */
  def getAndSet(a: A): USTM[A] =
    ZSTM.Effect { (journal, _, _) =>
      val entry    = getOrMakeEntry(journal)
      val oldValue = entry.unsafeGet[A]
      entry.unsafeSet(a)
      oldValue
    }

  /**
   * Updates the value of the variable and returns the old value.
   */
  def getAndUpdate(f: A => A): USTM[A] =
    ZSTM.Effect { (journal, _, _) =>
      val entry    = getOrMakeEntry(journal)
      val oldValue = entry.unsafeGet[A]
      entry.unsafeSet(f(oldValue))
      oldValue
    }

  /**
   * Updates some values of the variable but leaves others alone, returning the
   * old value.
   */
  def getAndUpdateSome(f: PartialFunction[A, A]): USTM[A] =
    getAndUpdate(f orElse { case a => a })

  /**
   * Updates the value of the variable, returning a function of the specified
   * value.
   */
  def modify[B](f: A => (B, A)): USTM[B] =
    ZSTM.Effect { (journal, _, _) =>
      val entry                = getOrMakeEntry(journal)
      val (retValue, newValue) = f(entry.unsafeGet[A])
      entry.unsafeSet(newValue)
      retValue
    }

  /**
   * Updates the value of the variable, returning a function of the specified
   * value.
   */
  def modifySome[B](default: B)(f: PartialFunction[A, (B, A)]): USTM[B] =
    modify(a => f.lift(a).getOrElse((default, a)))

  /**
   * Sets the value of the `TRef`.
   */
  def set(a: A): USTM[Unit] =
    ZSTM.Effect { (journal, _, _) =>
      val entry = getOrMakeEntry(journal)
      entry.unsafeSet(a)
      ()
    }

  override def toString: String =
    s"TRef(id = ${self.hashCode()}, versioned.value = ${versioned})"

  /**
   * Updates the value of the variable.
   */
  def update(f: A => A): USTM[Unit] =
    ZSTM.Effect { (journal, _, _) =>
      val entry = journal.getOrElseUpdate(self, newEntry())
      entry.unsafeUpdate(f.asInstanceOf[Any => Any])
      ()
    }

  /**
   * Updates the value of the variable and returns the new value.
   */
  def updateAndGet(f: A => A): USTM[A] =
    ZSTM.Effect { (journal, _, _) =>
      val entry    = getOrMakeEntry(journal)
      val newValue = f(entry.unsafeGet[A])
      entry.unsafeSet(newValue)
      newValue
    }

  /**
   * Updates some values of the variable but leaves others alone.
   */
  def updateSome(f: PartialFunction[A, A]): USTM[Unit] =
    update(f orElse { case a => a })

  /**
   * Updates some values of the variable but leaves others alone, returning the
   * new value.
   */
  def updateSomeAndGet(f: PartialFunction[A, A]): USTM[A] =
    updateAndGet(f orElse { case a => a })

  private[stm] def getOrMakeEntry(journal: Journal): Entry =
    journal.getOrElseUpdate(self, newEntry())

  private[stm] def unsafeGet(journal: Journal): A =
    getOrMakeEntry(journal).unsafeGet

  private[stm] def unsafeSet(journal: Journal, a: A): Unit =
    getOrMakeEntry(journal).unsafeSet(a)

  private[this] val newEntry = () => Entry(self.asInstanceOf[TRef[A & AnyRef]])
}

object TRef {
  private val idCounter: AtomicLong = new AtomicLong(0L)

  implicit val ordering: Ordering[TRef[?]] = new Ordering[TRef[?]] {
    def compare(x: TRef[?], y: TRef[?]): Int = java.lang.Long.compare(x.id, y.id)
  }

  /**
   * Makes a new `TRef` that is initialized to the specified value.
   */
  def make[A](a: => A): USTM[TRef[A]] =
    ZSTM.succeed(unsafe.make(a)(Unsafe.unsafe))

  /**
   * A convenience method that makes a `TRef` and immediately commits the
   * transaction to extract the value out.
   */
  def makeCommit[A](a: => A)(implicit trace: Trace): UIO[TRef[A]] =
    STM.atomically(make(a))

  object unsafe {
    def make[A](a: A)(implicit unsafe: Unsafe): TRef[A] =
      TRef.unsafeMake(a)
  }

  private[stm] def unsafeMake[A](a: A): TRef[A] =
    new TRef(new AtomicReference[A](a))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy