scala.concurrent.stm.japi.STM.scala Maven / Gradle / Ivy
/* scala-stm - (c) 2009-2012, Stanford University, PPL */
package scala.concurrent.stm.japi
import java.util.concurrent.{ Callable, TimeUnit }
import java.util.{ List ⇒ JList, Map ⇒ JMap, Set ⇒ JSet }
import scala.collection.JavaConversions
import scala.concurrent.stm
import scala.concurrent.stm._
private[japi] object STMHelpers {
// The anonymous classes generated by Scala 2.8.2 from inside a method with
// a type parameter are not digestible by Java, so hide them here to make
// sure they won't be included by:
//
// static import scala.concurrent.stm.japi.STM.*;
//
// Also, scala.Function1 is difficult to use from Java because Java won't
// automatically wire up all of the mix-in methods. A better option would
// be scala.runtime.AbstractFunction1, but there is no guarantee that the
// interface won't change (and Scala 2.8.2's version is not usable from
// Java). Instead, we define STM.Transformer that is basically just a
// Function1[A,A].
implicit def callableToAtomicBlock[A <: AnyRef](f: Callable[A]): (InTxn => A) = { _ => f.call }
implicit def transformerToFunction[A <: AnyRef](f: STM.Transformer[A]): (A => A) = { f(_) }
}
/**
* Java-friendly API for ScalaSTM.
* These methods can also be statically imported.
*/
object STM {
import STMHelpers._
/**
* Create a Ref with an initial value. Return a `Ref.View`, which does not
* require implicit transactions.
* @param initialValue the initial value for the newly created `Ref.View`
* @return a new `Ref.View`
*/
def newRef[A](initialValue: A): Ref.View[A] = Ref(initialValue).single
/**
* Create an empty TMap. Return a `TMap.View`, which does not require
* implicit transactions. See newMap for included java conversion.
* @return a new, empty `TMap.View`
*/
def newTMap[A, B](): TMap.View[A, B] = TMap.empty[A, B].single
/**
* Create an empty TMap. Return a `java.util.Map` view of this TMap.
* @return a new, empty `TMap.View` wrapped as a `java.util.Map`.
*/
def newMap[A, B](): JMap[A, B] = JavaConversions.asJavaMap(newTMap[A, B])
/**
* Create an empty TSet. Return a `TSet.View`, which does not require
* implicit transactions. See newSet for included java conversion.
* @return a new, empty `TSet.View`
*/
def newTSet[A](): TSet.View[A] = TSet.empty[A].single
/**
* Create an empty TSet. Return a `java.util.Set` view of this TSet.
* @return a new, empty `TSet.View` wrapped as a `java.util.Set`.
*/
def newSet[A](): JSet[A] = JavaConversions.asJavaSet(newTSet[A])
/**
* Create a TArray containing `length` elements. Return a `TArray.View`,
* which does not require implicit transactions. See newList for included
* java conversion.
* @param length the length of the `TArray.View` to be created
* @return a new `TArray.View` containing `length` elements (initially null)
*/
def newTArray[A <: AnyRef](length: Int): TArray.View[A] = TArray.ofDim[AnyRef](length).asInstanceOf[TArray[A]].single
/**
* Create an empty TArray. Return a `java.util.List` view of this Array.
* @param length the length of the `TArray.View` to be created
* @return a new, empty `TArray.View` wrapped as a `java.util.List`.
*/
def newArrayAsList[A <: AnyRef](length: Int): JList[A] = JavaConversions.asJavaList(newTArray[A](length))
/**
* Atomic block that takes a `Runnable`.
* @param runnable the `Runnable` to run within a transaction
*/
def atomic(runnable: Runnable): Unit = stm.atomic { txn ⇒ runnable.run }
/**
* Atomic block that takes a `Callable`.
* @param callable the `Callable` to run within a transaction
* @return the value returned by the `Callable`
*/
def atomic[A <: AnyRef](callable: Callable[A]): A = stm.atomic(callable)
/**
* Causes the enclosing transaction to back up and wait until one
* of the `Ref`s touched by this transaction has changed.
* @throws IllegalStateException if not in a transaction
*/
def retry(): Unit = Txn.findCurrent match {
case Some(txn) => Txn.retry(txn)
case None => throw new IllegalStateException("retry outside atomic")
}
/**
* Like `retry`, but limits the total amount of blocking. This method
* only returns normally when the timeout has expired.
*/
def retryFor(timeoutMillis: Long): Unit = Txn.findCurrent match {
case Some(txn) => Txn.retryFor(timeoutMillis)(txn)
case None => throw new IllegalStateException("retry outside atomic")
}
abstract class Transformer[A <: AnyRef] {
def apply(v: A): A
}
/**
* Transform the value stored by `ref` by applying the function `f`.
* @param ref the `Ref.View` to be transformed
* @param f the function to be applied
*/
def transform[A <: AnyRef](ref: Ref.View[A], f: Transformer[A]): Unit = ref.transform(f)
/**
* Transform the value stored by `ref` by applying the function `f` and
* return the old value.
* @param ref the `Ref.View` to be transformed
* @param f the function to be applied
* @return the old value of `ref`
*/
def getAndTransform[A <: AnyRef](ref: Ref.View[A], f: Transformer[A]): A = ref.getAndTransform(f)
/**
* Transform the value stored by `ref` by applying the function `f` and
* return the new value.
* @param ref the `Ref.View` to be transformed
* @param f the function to be applied
* @return the new value of `ref`
*/
def transformAndGet[A <: AnyRef](ref: Ref.View[A], f: Transformer[A]): A = ref.transformAndGet(f)
/**
* Increment the `java.lang.Integer` value of a `Ref.View`.
* @param ref the `Ref.View` to be incremented
* @param delta the amount to increment
*/
def increment(ref: Ref.View[java.lang.Integer], delta: Int): Unit = ref.transform { v ⇒ v.intValue + delta }
/**
* Increment the `java.lang.Long` value of a `Ref.View`.
* @param ref the `Ref.View` to be incremented
* @param delta the amount to increment
*/
def increment(ref: Ref.View[java.lang.Long], delta: Long): Unit = ref.transform { v ⇒ v.longValue + delta }
private def activeTxn = Txn.findCurrent match {
case Some(txn) => txn
case None => throw new IllegalStateException("not in a transaction")
}
/**
* Add a task to run after the current transaction has committed.
* @param task the `Runnable` task to run after transaction commit
* @throws IllegalStateException if called from outside a transaction
*/
def afterCommit(task: Runnable): Unit = Txn.afterCommit(status ⇒ task.run)(activeTxn)
/**
* Add a task to run after the current transaction has rolled back.
* @param task the `Runnable` task to run after transaction rollback
* @throws IllegalStateException if called from outside a transaction
*/
def afterRollback(task: Runnable): Unit = Txn.afterRollback(status ⇒ task.run)(activeTxn)
/**
* Add a task to run after the current transaction has either rolled back
* or committed.
* @param task the `Runnable` task to run after transaction completion
* @throws IllegalStateException if called from outside a transaction
*/
def afterCompletion(task: Runnable): Unit = Txn.afterCompletion(status ⇒ task.run)(activeTxn)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy