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

org.sincron.atomic.Atomic.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016 by its authors. Some rights reserved.
 * See the project homepage at: https://sincron.org
 *
 * 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 org.sincron.atomic

import org.sincron.macros._
import org.sincron.atomic.PaddingStrategy.NoPadding
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

/**
  * Base trait of all atomic references, no matter the type.
  */
abstract class Atomic[A] {
  /** Get the current value persisted by this Atomic. */
  def get: A

  /** Get the current value persisted by this Atomic, an alias for `get()`. */
  final def apply(): A = macro Atomic.Macros.applyMacro[A]

  /** Updates the current value.
    *
    * @param update will be the new value returned by `get()`
    */
  def set(update: A): Unit

  /** Alias for [[set]]. Updates the current value.
    *
    * @param value will be the new value returned by `get()`
    */
  final def update(value: A): Unit = macro Atomic.Macros.setMacro[A]

  /** Alias for [[set]]. Updates the current value.
    *
    * @param value will be the new value returned by `get()`
    */
  final def `:=`(value: A): Unit = macro Atomic.Macros.setMacro[A]

  /** Does a compare-and-set operation on the current value. For more info, checkout the related
    * [[https://en.wikipedia.org/wiki/Compare-and-swap Compare-and-swap Wikipedia page]].
    *
    * It's an atomic, worry free operation.
    *
    * @param expect is the value you expect to be persisted when the operation happens
    * @param update will be the new value, should the check for `expect` succeeds
    * @return either true in case the operation succeeded or false otherwise
    */
  def compareAndSet(expect: A, update: A): Boolean

  /** Sets the persisted value to `update` and returns the old value that was in place.
    * It's an atomic, worry free operation.
    */
  def getAndSet(update: A): A

  /** Eventually sets to the given value.
    * Has weaker visibility guarantees than the normal `set()`.
    */
  def lazySet(update: A): Unit

  /** Abstracts over `compareAndSet`. You specify a transformation by specifying a callback to be
    * executed, a callback that transforms the current value. This method will loop until it will
    * succeed in replacing the current value with the one produced by your callback.
    *
    * Note that the callback will be executed on each iteration of the loop, so it can be called
    * multiple times - don't do destructive I/O or operations that mutate global state in it.
    *
    * @param cb is a callback that receives the current value as input and returns a tuple that specifies
    *           the update + what should this method return when the operation succeeds.
    * @return whatever was specified by your callback, once the operation succeeds
    */
  final def transformAndExtract[U](cb: (A) => (U, A)): U =
    macro Atomic.Macros.transformAndExtractMacro[A, U]

  /** Abstracts over `compareAndSet`. You specify a transformation by specifying a callback to be
    * executed, a callback that transforms the current value. This method will loop until it will
    * succeed in replacing the current value with the one produced by the given callback.
    *
    * Note that the callback will be executed on each iteration of the loop, so it can be called
    * multiple times - don't do destructive I/O or operations that mutate global state in it.
    *
    * @param cb is a callback that receives the current value as input and returns the `update` which is the
    *           new value that should be persisted
    * @return whatever the update is, after the operation succeeds
    */
  final def transformAndGet(cb: (A) => A): A =
    macro Atomic.Macros.transformAndGetMacro[A]

  /** Abstracts over `compareAndSet`. You specify a transformation by specifying a callback to be
    * executed, a callback that transforms the current value. This method will loop until it will
    * succeed in replacing the current value with the one produced by the given callback.
    *
    * Note that the callback will be executed on each iteration of the loop, so it can be called
    * multiple times - don't do destructive I/O or operations that mutate global state in it.
    *
    * @param cb is a callback that receives the current value as input and returns the `update` which is the
    *           new value that should be persisted
    * @return the old value, just prior to when the successful update happened
    */
  final def getAndTransform(cb: (A) => A): A =
    macro Atomic.Macros.getAndTransformMacro[A]

  /** Abstracts over `compareAndSet`. You specify a transformation by specifying a callback to be
    * executed, a callback that transforms the current value. This method will loop until it will
    * succeed in replacing the current value with the one produced by the given callback.
    *
    * Note that the callback will be executed on each iteration of the loop, so it can be called
    * multiple times - don't do destructive I/O or operations that mutate global state in it.
    *
    * @param cb is a callback that receives the current value as input and returns the `update` which is the
    *           new value that should be persisted
    */
  final def transform(cb: (A) => A): Unit =
    macro Atomic.Macros.transformMacro[A]
}

object Atomic {
  /** Constructs an `Atomic[T]` reference. Based on the `initialValue`, it will return the best, most specific
    * type. E.g. you give it a number, it will return something inheriting from `AtomicNumber[T]`. That's why
    * it takes an `AtomicBuilder[T, R]` as an implicit parameter - but worry not about such details as it just works.
    *
    * @param initialValue is the initial value with which to initialize the Atomic reference
    * @param builder is the builder that helps us to build the best reference possible, based on our `initialValue`
    */
  def apply[T, R <: Atomic[T]](initialValue: T)(implicit builder: AtomicBuilder[T, R]): R =
    macro Atomic.Macros.buildAnyMacro[T, R]

  /** Constructs an `Atomic[T]` reference. Based on the `initialValue`, it will return the best, most specific
    * type. E.g. you give it a number, it will return something inheriting from `AtomicNumber[T]`. That's why
    * it takes an `AtomicBuilder[T, R]` as an implicit parameter - but worry not about such details as it just works.
    *
    * @param initialValue is the initial value with which to initialize the Atomic reference
    * @param padding is the [[PaddingStrategy]] to apply
    * @param builder is the builder that helps us to build the best reference possible, based on our `initialValue`
    */
  def withPadding[T, R <: Atomic[T]](initialValue: T, padding: PaddingStrategy)(implicit builder: AtomicBuilder[T, R]): R =
    macro Atomic.Macros.buildAnyWithPaddingMacro[T, R]

  /** Returns the builder that would be chosen to construct Atomic references
    * for the given `initialValue`.
    */
  def builderFor[T, R <: Atomic[T]](initialValue: T)(implicit builder: AtomicBuilder[T, R]): AtomicBuilder[T, R] =
    builder

  /** Macros implementations for the [[Atomic]] type */
  @macrocompat.bundle
  class Macros(override val c: whitebox.Context) extends HygieneUtilMacros with InlineMacros {
    import c.universe._

    def transformMacro[T : c.WeakTypeTag](cb: c.Expr[T => T]): c.Expr[Unit] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val self = util.name("self")
      val current = util.name("current")
      val update = util.name("update")

      /* If our arguments are all clean (stable identifiers or simple functions)
       * then inline them directly, otherwise bind arguments to a val for safety.
       */
      val tree =
        if (util.isClean(cb)) {
          q"""
          val $self = $selfExpr
          var $current = $self.get
          var $update = $cb($current)

          while (!$self.compareAndSet($current, $update)) {
            $current = $self.get
            $update = $cb($current)
          }
          """
        } else {
          val fn = util.name("fn")
          q"""
          val $self = $selfExpr
          val $fn = $cb

          var $current = $self.get
          var $update = $fn($current)

          while (!$self.compareAndSet($current, $update)) {
            $current = $self.get
            $update = $fn($current)
          }
          """
        }


      inlineAndReset[Unit](tree)
    }

    def transformAndGetMacro[T : c.WeakTypeTag](cb: c.Expr[T => T]): c.Expr[T] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val self = util.name("self")
      val current = util.name("current")
      val update = util.name("update")

      /* If our arguments are all clean (stable identifiers or simple functions)
       * then inline them directly, otherwise bind arguments to a val for safety.
       */
      val tree =
        if (util.isClean(cb)) {
          q"""
          val $self = $selfExpr
          var $current = $self.get
          var $update = $cb($current)

          while (!$self.compareAndSet($current, $update)) {
            $current = $self.get
            $update = $cb($current)
          }

          $update
          """
        } else {
          val fn = util.name("fn")
          q"""
          val $self = $selfExpr
          val $fn = $cb
          var $current = $self.get
          var $update = $fn($current)

          while (!$self.compareAndSet($current, $update)) {
            $current = $self.get
            $update = $fn($current)
          }

          $update
          """
        }

      inlineAndReset[T](tree)
    }

    def getAndTransformMacro[T : c.WeakTypeTag](cb: c.Expr[T => T]): c.Expr[T] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val self = util.name("self")
      val current = util.name("current")
      val update = util.name("update")

      /* If our arguments are all clean (stable identifiers or simple functions)
       * then inline them directly, otherwise bind arguments to a val for safety.
       */
      val tree =
        if (util.isClean(cb)) {
          q"""
          val $self = $selfExpr
          var $current = $self.get
          var $update = $cb($current)

          while (!$self.compareAndSet($current, $update)) {
            $current = $self.get
            $update = $cb($current)
          }

          $current
          """
        } else {
          val fn = util.name("fn")
          q"""
          val $self = $selfExpr
          val $fn = $cb
          var $current = $self.get
          var $update = $fn($current)

          while (!$self.compareAndSet($current, $update)) {
            $current = $self.get
            $update = $fn($current)
          }

          $current
          """
        }

      inlineAndReset[T](tree)
    }

    def transformAndExtractMacro[S : c.WeakTypeTag, A : c.WeakTypeTag]
      (cb: c.Expr[S => (A, S)]): c.Expr[A] = {

      val selfExpr = c.Expr[Atomic[S]](c.prefix.tree)
      val self = util.name("self")
      val current = util.name("current")
      val updateVar = util.name("updateVar")
      val resultVar = util.name("resultVar")
      val updateTmp = util.name("updateTmp")
      val resultTmp = util.name("resultTmp")

      /* If our arguments are all clean (stable identifiers or simple functions)
       * then inline them directly, otherwise bind arguments to a val for safety.
       */
      val tree =
        if (util.isClean(cb)) {
          q"""
          val $self = $selfExpr
          var $current = $self.get
          var ($resultVar, $updateVar) = $cb($current)

          while (!$self.compareAndSet($current, $updateVar)) {
            $current = $self.get
            val ($resultTmp, $updateTmp) = $cb($current)
            $updateVar = $updateTmp
            $resultVar = $resultTmp
          }

          $resultVar
          """
        } else {
          val fn = util.name("fn")
          q"""
          val $self = $selfExpr
          val $fn = $cb

          var $current = $self.get
          var ($resultVar, $updateVar) = $fn($current)

          while (!$self.compareAndSet($current, $updateVar)) {
            $current = $self.get
            val ($resultTmp, $updateTmp) = $fn($current)
            $updateVar = $updateTmp
            $resultVar = $resultTmp
          }

          $resultVar
          """
        }

      inlineAndReset[A](tree)
    }

    def buildAnyMacro[T : c.WeakTypeTag, R <: Atomic[T] : c.WeakTypeTag]
      (initialValue: c.Expr[T])
      (builder: c.Expr[AtomicBuilder[T, R]]): c.Expr[R] = {

      val expr = reify {
        builder.splice.buildInstance(initialValue.splice, NoPadding)
      }

      inlineAndReset[R](expr.tree)
    }

    def buildAnyWithPaddingMacro[T : c.WeakTypeTag, R <: Atomic[T] : c.WeakTypeTag]
      (initialValue: c.Expr[T], padding: c.Expr[PaddingStrategy])
      (builder: c.Expr[AtomicBuilder[T, R]]): c.Expr[R] = {

      val expr = reify {
        builder.splice.buildInstance(initialValue.splice, padding.splice)
      }

      inlineAndReset[R](expr.tree)
    }

    def applyMacro[T : c.WeakTypeTag](): c.Expr[T] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val tree = q"""$selfExpr.get"""
      inlineAndReset[T](tree)
    }

    def setMacro[T : c.WeakTypeTag](value: c.Expr[T]): c.Expr[Unit] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val tree = q"""$selfExpr.set($value)"""
      inlineAndReset[Unit](tree)
    }

    def addMacro[T : c.WeakTypeTag](value: c.Expr[T]): c.Expr[Unit] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val tree = q"""$selfExpr.add($value)"""
      inlineAndReset[Unit](tree)
    }

    def subtractMacro[T : c.WeakTypeTag](value: c.Expr[T]): c.Expr[Unit] = {
      val selfExpr = c.Expr[Atomic[T]](c.prefix.tree)
      val tree = q"""$selfExpr.subtract($value)"""
      inlineAndReset[Unit](tree)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy