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

monix.execution.AsyncVar.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014-2019 by The Monix Project Developers.
 * See the project homepage at: https://monix.io
 *
 * 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 monix.execution

import monix.execution.annotations.{UnsafeBecauseImpure, UnsafeProtocol}
import monix.execution.atomic.PaddingStrategy
import monix.execution.atomic.PaddingStrategy.NoPadding
import monix.execution.internal.GenericVar
import monix.execution.internal.GenericVar.Id
import scala.concurrent.Promise

/** Asynchronous mutable location, that is either empty or contains
  * a value of type `A`.
  *
  * It has these fundamental atomic operations:
  *
  *  - [[put]] which fills the var if empty, or waits
  *    (asynchronously) otherwise until the var is empty again
  *    (with the [[putByCallback]] overload)
  *
  *  - [[tryPut]] which fills the var if empty, returning `true`
  *    if it succeeded, or returning immediately `false` in case
  *    the var was full and thus the operation failed
  *
  *  - [[take]] which empties the var if full, returning the contained
  *    value, or waits (asynchronously) otherwise until there is
  *    a value to pull (with the [[takeByCallback]] overload)
  *
  *  - [[tryTake]] which empties the var if full, returning the
  *    contained value immediately as `Some(a)`, or otherwise returning
  *    `None` in case the var was empty and thus the operation failed
  *
  *  - [[read]] which reads the var if full, but without taking it
  *    from the interval var, or waits (asynchronously) until
  *    there is a value to read
  *
  *  - [[tryRead]] tries reading the var without modifying it in
  *    any way; if full then returns `Some(a)`, or `None` if empty
  *
  * The `AsyncVar` is appropriate for building synchronization
  * primitives and performing simple inter-thread communications.
  * If it helps, it's similar with a `BlockingQueue(capacity = 1)`,
  * except that it doesn't block any threads, all waiting being
  * callback-based.
  *
  * Given its asynchronous, non-blocking nature, it can be used on
  * top of Javascript as well.
  *
  * This is inspired by
  * [[https://hackage.haskell.org/package/base/docs/Control-Concurrent-MVar.html Control.Concurrent.MVar]]
  * from Haskell, except that the implementation is made to work with
  * plain Scala futures (and is thus impure).
  *
  * @define awaitParam is a callback that will be called when the
  *         operation succeeded with a result
  *
  * @define cancelableReturn a cancelable token that can be used to cancel
  *         the computation to avoid memory leaks in race conditions
  */
final class AsyncVar[A] private (initial: Option[A], ps: PaddingStrategy)
  extends GenericVar[A, Cancelable](initial, ps) {

  private def this(ps: PaddingStrategy) =
    this(None, ps)
  private def this(initial: A, ps: PaddingStrategy) =
    this(Some(initial), ps)

  override protected def makeCancelable(f: Id => Unit, id: Id): Cancelable =
    new Cancelable { def cancel() = f(id) }
  override protected def emptyCancelable: Cancelable =
    Cancelable.empty

  /** Fills the `AsyncVar` if it is empty, or blocks (asynchronously)
    * if the `AsyncVar` is full, until the given value is next in
    * line to be consumed on [[take]].
    *
    * This operation is atomic.
    *
    * @see [[putByCallback]] for the raw, unsafe version that can work with
    *      plain callbacks.
    *
    * @return a future that will complete when the `put` operation
    *         succeeds in filling the `AsyncVar`, with the given
    *         value being next in line to be consumed; note that this
    *         is a cancelable future that can be canceled to avoid
    *         memory leaks in race conditions
    */
  @UnsafeBecauseImpure
  def put(a: A): CancelableFuture[Unit] = {
    val p = Promise[Unit]()
    val c = putByCallback(a, Callback.fromPromise(p))
    CancelableFuture(p.future, c)
  }

  /** Fills the `AsyncVar` if it is empty, or blocks (asynchronously)
    * if the `AsyncVar` is full, until the given value is next in
    * line to be consumed on [[take]].
    *
    * This operation is atomic.
    *
    * @see [[put]] for the safe future-enabled version.
    *
    * @param a is the value to store
    * @param await $awaitParam
    * @return $cancelableReturn
    */
  @UnsafeProtocol
  @UnsafeBecauseImpure
  def putByCallback(a: A, await: Callback[Nothing, Unit]): Cancelable =
    unsafePut(a, await)

  /**
    * Tries to put a value in the underlying var, returning `true` if the
    * operation succeeded and thus the var was empty, or `false` if the
    * var was full and thus the operation failed.
    *
    * @see [[put]] for the version that can asynchronously wait for the
    *      var to become empty
    */
  @UnsafeProtocol
  @UnsafeBecauseImpure
  def tryPut(a: A): Boolean = unsafeTryPut(a)

  /** Empties the var if full, returning the contained value,
    * or blocks (asynchronously) until a value is available.
    *
    * This operation is atomic.
    *
    * @see [[takeByCallback]] for the raw, unsafe version that can work
    *     with plain callbacks.
    */
  @UnsafeBecauseImpure
  def take(): CancelableFuture[A] = {
    val p = Promise[A]()
    val c = takeByCallback(Callback.fromPromise(p))
    CancelableFuture(p.future, c)
  }

  /** Empties the var if full, returning the contained value,
    * or blocks (asynchronously) until a value is available.
    *
    * This operation is atomic.
    *
    * @see [[take]] for the safe future-enabled version.
    *
    * @param await $awaitParam
    * @return $cancelableReturn
    */
  @UnsafeProtocol
  @UnsafeBecauseImpure
  def takeByCallback(await: Callback[Nothing, A]): Cancelable =
    unsafeTake(await)

  /**
    * Tries to take a value from the underlying var, returning `Some(a)` if the
    * operation succeeded and thus the var was full, or `None` if the
    * var was empty and thus the operation failed.
    *
    * @see [[take]] for the version that can asynchronously wait for the
    *      var to become full
    */
  @UnsafeProtocol
  @UnsafeBecauseImpure
  def tryTake(): Option[A] = unsafeTryTake()

  /** Tries reading the current value, or waits (asynchronously)
    * until there is a value available.
    *
    * This operation is atomic.
    *
    * @see [[readByCallback]] for the raw, unsafe version that can work
    *     with plain callbacks.
    *
    * @return a future that might already be completed in case the
    *         result is available immediately
    */
  @UnsafeBecauseImpure
  def read(): CancelableFuture[A] = {
    val p = Promise[A]()
    val c = readByCallback(Callback.fromPromise(p))
    CancelableFuture(p.future, c)
  }

  /** Tries reading the current value, or waits (asynchronously)
    * until there is a value available.
    *
    * This operation is atomic.
    *
    * @see [[read]] for the safe future-enabled version.
    *
    * @param await $awaitParam
    * @return $cancelableReturn
    */
  @UnsafeProtocol @UnsafeBecauseImpure
  def readByCallback(await: Callback[Nothing, A]): Cancelable =
    unsafeRead(await)

  /**
    * Tries reading the current value, without modifying the var in any way:
    *
    *  - if full, returns `Some(a)`
    *  - if empty, returns `None`
    */
  @UnsafeProtocol
  @UnsafeBecauseImpure
  def tryRead(): Option[A] = unsafeTryRead()

  /**
    * Returns `true` if the var is empty, `false` otherwise.
    *
    */
  @UnsafeProtocol
  @UnsafeBecauseImpure
  def iEmpty(): Boolean = unsafeIsEmpty()
}

object AsyncVar {
  /** Builds an [[AsyncVar]] instance with an `initial` value.
    *
    * @param initial is the initial value, this var being initialized
    *        full; see [[empty]] for the alternative
    *
    * @param ps is an optional padding strategy for avoiding the
    *        "false sharing problem", a common JVM effect when multiple threads
    *        read and write in shared variables
    */
  def apply[A](initial: A, ps: PaddingStrategy = NoPadding): AsyncVar[A] =
    new AsyncVar[A](initial, ps)

  /** Returns an empty [[AsyncVar]] instance.
    *
    * @param ps is an optional padding strategy for avoiding the
    *        "false sharing problem", a common JVM effect when multiple threads
    *        read and write in shared variables
    */
  def empty[A](ps: PaddingStrategy = NoPadding): AsyncVar[A] =
    new AsyncVar[A](ps)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy