pekko/kernel/src/main/scala/stm/TransactionManagement.scala

154 lines
5.4 KiB
Scala
Raw Normal View History

/**
* Copyright (C) 2009 Scalable Solutions.
*/
package se.scalablesolutions.akka.kernel.stm
import java.lang.reflect.Field
import java.util.concurrent.atomic.AtomicBoolean
import kernel.state.{TransactionalMap, TransactionalRef, TransactionalVector}
import kernel.util.Logging
class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Transaction]) extends RuntimeException(cause) {
override def toString(): String = "TransactionAwareWrapperException[" + cause + ", " + tx + "]"
}
object TransactionManagement {
private val txEnabled = new AtomicBoolean(true)
def isTransactionsEnabled = txEnabled.get
def enableTransactions = txEnabled.set(true)
private[kernel] lazy val threadBoundTx: ThreadLocal[Option[Transaction]] = {
val tl = new ThreadLocal[Option[Transaction]]
tl.set(None)
tl
}
}
// FIXME: STM that allows concurrent updates, detects collision, rolls back and restarts
trait TransactionManagement extends Logging {
val transactionalInstance: AnyRef
private lazy val changeSet = new ChangeSet(transactionalInstance.getClass.getName)
import TransactionManagement.threadBoundTx
private[kernel] var activeTx: Option[Transaction] = None
protected def startNewTransaction = {
val (maps, vectors, refs) = getTransactionalItemsFor(transactionalInstance)
changeSet.maps = maps
changeSet.refs = refs
changeSet.vectors = vectors
val newTx = new Transaction
newTx.begin(changeSet)
val tx = Some(newTx)
activeTx = tx
threadBoundTx.set(tx)
}
protected def joinExistingTransaction = {
val cflowTx = threadBoundTx.get
if (activeTx.isDefined && cflowTx.isDefined && activeTx.get.id == cflowTx.get.id) {
val currentTx = cflowTx.get
currentTx.join(changeSet)
activeTx = Some(currentTx)
}
}
protected def tryToPrecommitTransaction = if (activeTx.isDefined) activeTx.get.precommit(changeSet)
protected def tryToCommitTransaction: Boolean = if (activeTx.isDefined) {
val tx = activeTx.get
tx.commit(changeSet)
removeTransactionIfTopLevel
true
} else false
protected def rollback(tx: Option[Transaction]) = tx match {
case None => {} // no tx; nothing to do
case Some(tx) =>
tx.rollback(changeSet)
}
protected def isInExistingTransaction = {
println(TransactionManagement)
println(TransactionManagement.threadBoundTx)
println(TransactionManagement.threadBoundTx.get)
println(TransactionManagement.threadBoundTx.get.isDefined)
TransactionManagement.threadBoundTx.get.isDefined
}
protected def isTransactionAborted = activeTx.isDefined && activeTx.get.isAborted
protected def incrementTransaction = if (activeTx.isDefined) activeTx.get.increment
protected def decrementTransaction = if (activeTx.isDefined) activeTx.get.decrement
protected def removeTransactionIfTopLevel =
if (activeTx.isDefined && activeTx.get.topLevel_?) {
activeTx = None
threadBoundTx.set(None)
}
protected def reenteringExistingTransaction= if (activeTx.isDefined) {
val cflowTx = threadBoundTx.get
if (cflowTx.isDefined && cflowTx.get.id == activeTx.get.id) false
else true
} else true
/**
* Search for transactional items for a specific target instance, crawl the class hierarchy recursively up to the top.
*/
protected def getTransactionalItemsFor(targetInstance: AnyRef):
Tuple3[List[TransactionalMap[_, _]], List[TransactionalVector[_]], List[TransactionalRef[_]]] = {
require(targetInstance != null)
var maps: List[TransactionalMap[_, _]] = Nil
var refs: List[TransactionalRef[_]] = Nil
var vectors: List[TransactionalVector[_]] = Nil
def getTransactionalItemsFor(target: Class[_]):
Tuple3[List[TransactionalMap[_, _]], List[TransactionalVector[_]], List[TransactionalRef[_]]] = {
for {
field <- target.getDeclaredFields.toArray.toList.asInstanceOf[List[Field]]
fieldType = field.getType
if (fieldType == classOf[TransactionalMap[_, _]]) ||
(fieldType == classOf[TransactionalVector[_]]) ||
(fieldType == classOf[TransactionalRef[_]])
txItem = {
field.setAccessible(true)
field.get(targetInstance)
}
if txItem != null
} {
log.debug("Managing transactional state [%s]", field)
if (txItem.isInstanceOf[TransactionalMap[_, _]]) maps ::= txItem.asInstanceOf[TransactionalMap[_, _]]
else if (txItem.isInstanceOf[TransactionalRef[_]]) refs ::= txItem.asInstanceOf[TransactionalRef[_]]
else if (txItem.isInstanceOf[TransactionalVector[_]]) vectors ::= txItem.asInstanceOf[TransactionalVector[_]]
}
val parent = target.getSuperclass
if (parent == classOf[Object]) (maps, vectors, refs)
else getTransactionalItemsFor(parent)
}
// start the search for transactional items, crawl the class hierarchy up until we reach Object
getTransactionalItemsFor(targetInstance.getClass)
}
/*
protected def getResultOrThrowException[T](future: FutureResult): Option[T] =
if (future.exception.isDefined) {
val (_, cause) = future.exception.get
throw new TransactionAwareWrapperException(cause, activeTx)
} else {
if (future.result.isDefined) {
val (res, tx) = future.result.get.asInstanceOf[Tuple2[AnyRef, Option[Transaction]]]
Some(res).asInstanceOf[Option[T]]
} else None
}
*/
}