Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2017-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
import zio.stacktracer.TracingImplicits.disableAutoTrace
import java.util.concurrent.atomic.AtomicReference
/**
* A `Ref` is a purely functional description of a mutable reference. The
* fundamental operations of a `Ref` are `set` and `get`. `set` sets the
* reference to a new value. `get` gets the current value of the reference.
*
* By default, `Ref` is implemented in terms of compare and swap operations for
* maximum performance and does not support performing effects within update
* operations. If you need to perform effects within update operations you can
* create a `Ref.Synchronized`, a specialized type of `Ref` that supports
* performing effects within update operations at some cost to performance. In
* this case writes will semantically block other writers, while multiple
* readers can read simultaneously.
*
* NOTE: While `Ref` provides the functional equivalent of a mutable reference,
* the value inside the `Ref` should normally be immutable since compare and
* swap operations are not safe for mutable values that do not support
* concurrent access. If you do need to use a mutable value `Ref.Synchronized`
* will guarantee that access to the value is properly synchronized.
*/
abstract class Ref[A] extends Serializable {
/**
* Reads the value from the `Ref`.
*/
def get(implicit trace: Trace): UIO[A]
/**
* Atomically modifies the `Ref` with the specified function, which computes a
* return value for the modification. This is a more powerful version of
* `update`.
*/
def modify[B](f: A => (B, A))(implicit trace: Trace): UIO[B]
/**
* Writes a new value to the `Ref`, with a guarantee of immediate consistency
* (at some cost to performance).
*/
def set(a: A)(implicit trace: Trace): UIO[Unit]
/**
* Writes a new value to the `Ref` without providing a guarantee of immediate
* consistency.
*/
def setAsync(a: A)(implicit trace: Trace): UIO[Unit]
/**
* Atomically writes the specified value to the `Ref`, returning the value
* immediately before modification.
*/
final def getAndSet(a: A)(implicit trace: Trace): UIO[A] =
modify(v => (v, a))
/**
* Atomically modifies the `Ref` with the specified function, returning the
* value immediately before modification.
*/
final def getAndUpdate(f: A => A)(implicit trace: Trace): UIO[A] =
modify(v => (v, f(v)))
/**
* Atomically modifies the `Ref` with the specified partial function,
* returning the value immediately before modification. If the function is
* undefined on the current value it doesn't change it.
*/
final def getAndUpdateSome(pf: PartialFunction[A, A])(implicit trace: Trace): UIO[A] =
modify { v =>
val result = pf.applyOrElse[A, A](v, identity)
(v, result)
}
/**
* Atomically modifies the `Ref` with the specified partial function, which
* computes a return value for the modification if the function is defined on
* the current value otherwise it returns a default value. This is a more
* powerful version of `updateSome`.
*/
final def modifySome[B](default: B)(pf: PartialFunction[A, (B, A)])(implicit trace: Trace): UIO[B] =
modify(v => pf.applyOrElse[A, (B, A)](v, _ => (default, v)))
/**
* Atomically modifies the `Ref` with the specified function.
*/
final def update(f: A => A)(implicit trace: Trace): UIO[Unit] =
modify(v => ((), f(v)))
/**
* Atomically modifies the `Ref` with the specified function and returns the
* updated value.
*/
final def updateAndGet(f: A => A)(implicit trace: Trace): UIO[A] =
modify { v =>
val result = f(v)
(result, result)
}
/**
* Atomically modifies the `Ref` with the specified partial function. If the
* function is undefined on the current value it doesn't change it.
*/
final def updateSome(pf: PartialFunction[A, A])(implicit trace: Trace): UIO[Unit] =
modify { v =>
val result = pf.applyOrElse[A, A](v, identity)
((), result)
}
/**
* Atomically modifies the `Ref` with the specified partial function. If the
* function is undefined on the current value it returns the old value without
* changing it.
*/
final def updateSomeAndGet(pf: PartialFunction[A, A])(implicit trace: Trace): UIO[A] =
modify { v =>
val result = pf.applyOrElse[A, A](v, identity)
(result, result)
}
}
object Ref extends Serializable {
/**
* Creates a new `Ref` with the specified value.
*/
def make[A](a: => A)(implicit trace: Trace): UIO[Ref[A]] =
ZIO.succeed(unsafe.make(a)(Unsafe.unsafe))
object unsafe {
def make[A](a: A)(implicit unsafe: Unsafe): Ref.Atomic[A] =
Atomic(new AtomicReference(a))
}
/**
* A `Ref.Synchronized` is a purely functional description of a mutable
* reference. The fundamental operations of a `Ref.Synchronized` are `set` and
* `get`. `set` sets the reference to a new value. `get` gets the current
* value of the reference.
*
* Unlike an ordinary `Ref`, a `Ref.Synchronized` allows performing effects
* within update operations, at some cost to performance. Writes will
* semantically block other writers, while multiple readers can read
* simultaneously.
*/
abstract class Synchronized[A] extends Ref[A] {
/**
* Reads the value from the `Ref`.
*/
def get(implicit trace: Trace): UIO[A]
/**
* Atomically modifies the `Ref.Synchronized` with the specified function,
* which computes a return value for the modification. This is a more
* powerful version of `update`.
*/
def modifyZIO[R, E, B](f: A => ZIO[R, E, (B, A)])(implicit trace: Trace): ZIO[R, E, B]
/**
* Writes a new value to the `Ref`, with a guarantee of immediate
* consistency (at some cost to performance).
*/
def set(a: A)(implicit trace: Trace): UIO[Unit]
/**
* Writes a new value to the `Ref` without providing a guarantee of
* immediate consistency.
*/
def setAsync(a: A)(implicit trace: Trace): UIO[Unit]
/**
* Atomically modifies the `Ref.Synchronized` with the specified function,
* returning the value immediately before modification.
*/
def getAndUpdateZIO[R, E](f: A => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
modifyZIO(v => f(v).map(result => (v, result)))
/**
* Atomically modifies the `Ref.Synchronized` with the specified partial
* function, returning the value immediately before modification. If the
* function is undefined on the current value it doesn't change it.
*/
def getAndUpdateSomeZIO[R, E](pf: PartialFunction[A, ZIO[R, E, A]])(implicit
trace: Trace
): ZIO[R, E, A] =
modifyZIO(v => pf.applyOrElse[A, ZIO[R, E, A]](v, ZIO.succeed(_)).map(result => (v, result)))
final def modify[B](f: A => (B, A))(implicit trace: Trace): UIO[B] =
modifyZIO(a => ZIO.succeed(f(a)))
/**
* Atomically modifies the `Ref.Synchronized` with the specified function,
* which computes a return value for the modification if the function is
* defined in the current value otherwise it returns a default value. This
* is a more powerful version of `updateSome`.
*/
def modifySomeZIO[R, E, B](default: B)(pf: PartialFunction[A, ZIO[R, E, (B, A)]])(implicit
trace: Trace
): ZIO[R, E, B] =
modifyZIO(v => pf.applyOrElse[A, ZIO[R, E, (B, A)]](v, _ => ZIO.succeed((default, v))))
/**
* Atomically modifies the `Ref.Synchronized` with the specified function.
*/
def updateZIO[R, E](f: A => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, Unit] =
modifyZIO(v => f(v).map(result => ((), result)))
/**
* Atomically modifies the `Ref.Synchronized` with the specified function,
* returning the value immediately after modification.
*/
def updateAndGetZIO[R, E](f: A => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
modifyZIO(v => f(v).map(result => (result, result)))
/**
* Atomically modifies the `Ref.Synchronized` with the specified partial
* function. If the function is undefined on the current value it doesn't
* change it.
*/
def updateSomeZIO[R, E](pf: PartialFunction[A, ZIO[R, E, A]])(implicit
trace: Trace
): ZIO[R, E, Unit] =
modifyZIO(v => pf.applyOrElse[A, ZIO[R, E, A]](v, ZIO.succeed(_)).map(result => ((), result)))
/**
* Atomically modifies the `Ref.Synchronized` with the specified partial
* function. If the function is undefined on the current value it returns
* the old value without changing it.
*/
def updateSomeAndGetZIO[R, E](pf: PartialFunction[A, ZIO[R, E, A]])(implicit
trace: Trace
): ZIO[R, E, A] =
modifyZIO(v => pf.applyOrElse[A, ZIO[R, E, A]](v, ZIO.succeed(_)).map(result => (result, result)))
}
object Synchronized {
/**
* Creates a new `Ref.Synchronized` with the specified value.
*/
def make[A](a: => A)(implicit trace: Trace): UIO[Synchronized[A]] =
ZIO.succeed(unsafe.make(a)(Unsafe.unsafe))
object unsafe {
def make[A](a: A)(implicit unsafe: Unsafe): Synchronized[A] = {
val ref = Ref.unsafe.make(a)
val semaphore = Semaphore.unsafe.make(1)
new Ref.Synchronized[A] {
def get(implicit trace: Trace): UIO[A] =
ref.get
def modifyZIO[R, E, B](f: A => ZIO[R, E, (B, A)])(implicit trace: Trace): ZIO[R, E, B] =
semaphore.withPermit(get.flatMap(f).flatMap { case (b, a) => ref.set(a).as(b) })
def set(a: A)(implicit trace: Trace): UIO[Unit] =
semaphore.withPermit(ref.set(a))
def setAsync(a: A)(implicit trace: Trace): UIO[Unit] =
semaphore.withPermit(ref.setAsync(a))
}
}
}
}
private[zio] final case class Atomic[A](value: AtomicReference[A]) extends Ref[A] {
self =>
def get(implicit trace: Trace): UIO[A] =
ZIO.succeed(unsafe.get(Unsafe.unsafe))
def modify[B](f: A => (B, A))(implicit trace: Trace): UIO[B] =
ZIO.succeed(unsafe.modify(f)(Unsafe.unsafe))
def set(a: A)(implicit trace: Trace): UIO[Unit] =
ZIO.succeed(unsafe.set(a)(Unsafe.unsafe))
def setAsync(a: A)(implicit trace: Trace): UIO[Unit] =
ZIO.succeed(unsafe.setAsync(a)(Unsafe.unsafe))
override def toString: String =
s"Ref(${value.get})"
trait UnsafeAPI {
def get(implicit unsafe: Unsafe): A
def getAndSet(a: A)(implicit unsafe: Unsafe): A
def getAndUpdate(f: A => A)(implicit unsafe: Unsafe): A
def getAndUpdateSome(pf: PartialFunction[A, A])(implicit unsafe: Unsafe): A
def modify[B](f: A => (B, A))(implicit unsafe: Unsafe): B
def modifySome[B](default: B)(pf: PartialFunction[A, (B, A)])(implicit unsafe: Unsafe): B
def set(a: A)(implicit unsafe: Unsafe): Unit
def setAsync(a: A)(implicit unsafe: Unsafe): Unit
def update(f: A => A)(implicit unsafe: Unsafe): Unit
def updateAndGet(f: A => A)(implicit unsafe: Unsafe): A
def updateSome(pf: PartialFunction[A, A])(implicit unsafe: Unsafe): Unit
def updateSomeAndGet(pf: PartialFunction[A, A])(implicit unsafe: Unsafe): A
}
@transient lazy val unsafe: UnsafeAPI =
new UnsafeAPI {
def get(implicit unsafe: Unsafe): A =
value.get
def getAndSet(a: A)(implicit unsafe: Unsafe): A = {
var loop = true
var current: A = null.asInstanceOf[A]
while (loop) {
current = value.get
loop = !value.compareAndSet(current, a)
}
current
}
def getAndUpdate(f: A => A)(implicit unsafe: Unsafe): A = {
var loop = true
var current: A = null.asInstanceOf[A]
while (loop) {
current = value.get
val next = f(current)
loop = !value.compareAndSet(current, next)
}
current
}
def getAndUpdateSome(pf: PartialFunction[A, A])(implicit unsafe: Unsafe): A = {
var loop = true
var current: A = null.asInstanceOf[A]
while (loop) {
current = value.get
val next = pf.applyOrElse(current, (_: A) => current)
loop = !value.compareAndSet(current, next)
}
current
}
def modify[B](f: A => (B, A))(implicit unsafe: Unsafe): B = {
var loop = true
var b: B = null.asInstanceOf[B]
while (loop) {
val current = value.get
val tuple = f(current)
b = tuple._1
loop = !value.compareAndSet(current, tuple._2)
}
b
}
def modifySome[B](default: B)(pf: PartialFunction[A, (B, A)])(implicit unsafe: Unsafe): B = {
var loop = true
var b: B = null.asInstanceOf[B]
while (loop) {
val current = value.get
val tuple = pf.applyOrElse(current, (_: A) => (default, current))
b = tuple._1
loop = !value.compareAndSet(current, tuple._2)
}
b
}
def set(a: A)(implicit unsafe: Unsafe): Unit =
value.set(a)
def setAsync(a: A)(implicit unsafe: Unsafe): Unit =
value.lazySet(a)
def update(f: A => A)(implicit unsafe: Unsafe): Unit = {
var loop = true
var next: A = null.asInstanceOf[A]
while (loop) {
val current = value.get
next = f(current)
loop = !value.compareAndSet(current, next)
}
()
}
def updateAndGet(f: A => A)(implicit unsafe: Unsafe): A = {
var loop = true
var next: A = null.asInstanceOf[A]
while (loop) {
val current = value.get
next = f(current)
loop = !value.compareAndSet(current, next)
}
next
}
def updateSome(pf: PartialFunction[A, A])(implicit unsafe: Unsafe): Unit = {
var loop = true
var next: A = null.asInstanceOf[A]
while (loop) {
val current = value.get
next = pf.applyOrElse(current, (_: A) => current)
loop = !value.compareAndSet(current, next)
}
()
}
def updateSomeAndGet(pf: PartialFunction[A, A])(implicit unsafe: Unsafe): A = {
var loop = true
var next: A = null.asInstanceOf[A]
while (loop) {
val current = value.get
next = pf.applyOrElse(current, (_: A) => current)
loop = !value.compareAndSet(current, next)
}
next
}
}
}
}