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

scala.concurrent.stm.skel.AbstractInTxn.scala Maven / Gradle / Ivy

/* scala-stm - (c) 2009-2011, Stanford University, PPL */

package scala.concurrent.stm
package skel

import annotation.tailrec

private[stm] trait AbstractInTxn extends InTxn {
  import Txn._

  /** This is different from `currentLevel` so that `InTxn` instances can
   *  assume that the return value from `topLevel` is not exposed to the user.
   */
  protected def internalCurrentLevel: AbstractNestingLevel

  //////////// implementation of functionality for the InTxn implementer

  protected def requireActive() {
    internalCurrentLevel.status match {
      case Active =>
      case RolledBack(_) => throw RollbackError
      case s => throw new IllegalStateException(s.toString)
    }
  }

  protected def requireNotDecided() {
    internalCurrentLevel.status match {
      case s if !s.decided =>
      case RolledBack(_) => throw RollbackError
      case s => throw new IllegalStateException(s.toString)
    }
  }

  protected def requireNotCompleted() {
    internalCurrentLevel.status match {
      case s if !s.completed =>
      case RolledBack(_) => throw RollbackError
      case s => throw new IllegalStateException(s.toString)
    }
  }

  private var _decider: ExternalDecider = null
  protected def externalDecider = _decider

  /** Set to true if any callbacks are registered. */
  private var _callbacksPresent = false
  
  private val _beforeCommitList = new CallbackList[InTxn]
  private val _whileValidatingList = new CallbackList[NestingLevel]
  private val _whilePreparingList = new CallbackList[InTxnEnd]
  private val _whileCommittingList = new CallbackList[InTxnEnd]
  private val _afterCommitList = new CallbackList[Status]
  private val _afterRollbackList = new CallbackList[Status]

  /** Returns true if there are while-preparing handlers, while-committing
   *  handlers, or an external decider.
   */
  protected def writeResourcesPresent: Boolean = _callbacksPresent && writeResourcesPresentImpl

  private def writeResourcesPresentImpl: Boolean = {
    !_whilePreparingList.isEmpty || !_whileCommittingList.isEmpty || externalDecider != null
  }

  protected def fireBeforeCommitCallbacks() {
    if (_callbacksPresent)
      _beforeCommitList.fire(internalCurrentLevel, this)
  }

  protected def fireWhilePreparingCallbacks() {
    if (_callbacksPresent && !_whilePreparingList.isEmpty)
      _whilePreparingList.fire(internalCurrentLevel, this)
  }

  protected def fireWhileCommittingCallbacks(exec: TxnExecutor): Throwable = {
    if (_callbacksPresent && !_whileCommittingList.isEmpty)
      fireWhileCommittingCallbacksImpl(exec)
    else
      null
  }

  private def fireWhileCommittingCallbacksImpl(exec: TxnExecutor): Throwable = {
    var failure: Throwable = null
    var i = 0
    while (i < _whileCommittingList.size) {
      failure = firePostDecision(_whileCommittingList(i), this, exec, Txn.Committing, failure)
      i += 1
    }
    failure
  }

  protected def fireAfterCompletionAndThrow(handlers: Array[Status => Unit], exec: TxnExecutor, s: Status, pendingFailure: Throwable) {
    val f = if (handlers != null) fireAfterCompletion(handlers, exec, s, pendingFailure) else pendingFailure
    if (f != null)
      throw f
  }

  protected def fireAfterCompletion(handlers: Array[Status => Unit], exec: TxnExecutor, s: Status, f0: Throwable): Throwable = {
    var failure = f0
    var i = 0
    val inOrder = s eq Committed
    var j = if (inOrder) 0 else handlers.length - 1
    var dj = if (inOrder) 1 else -1
    while (i < handlers.length) {
      failure = firePostDecision(handlers(j), s, exec, s, failure)
      i += 1
      j += dj
    }
    failure
  }

  private def firePostDecision[A](handler: A => Unit, arg: A, exec: TxnExecutor, s: Status, f: Throwable): Throwable = {
    try {
      handler(arg)
      f
    } catch {
      case x: Throwable => {
        try {
          exec.postDecisionFailureHandler(s, x)
          f
        } catch {
          case xx: Throwable => xx
        }
      }
    }
  }

  protected def checkpointCallbacks() {
    if (_callbacksPresent)
      checkpointCallbacksImpl()
  }

  private def checkpointCallbacksImpl() {
    val level = internalCurrentLevel
    level._beforeCommitSize = _beforeCommitList.size
    level._whileValidatingSize = _whileValidatingList.size
    level._whilePreparingSize = _whilePreparingList.size
    level._whileCommittingSize = _whileCommittingList.size
    level._afterCommitSize = _afterCommitList.size
    level._afterRollbackSize = _afterRollbackList.size
  }

  /** Returns the discarded `afterRollbackList` entries, or null if none */
  protected def rollbackCallbacks(): Array[Status => Unit] = {
    if (!_callbacksPresent) null else rollbackCallbacksImpl()
  }

  private def rollbackCallbacksImpl(): Array[Status => Unit] = {
    val level = internalCurrentLevel
    _beforeCommitList.size = level._beforeCommitSize
    _whileValidatingList.size = level._whileValidatingSize
    _whilePreparingList.size = level._whilePreparingSize
    _whileCommittingList.size = level._whileCommittingSize
    _afterCommitList.size = level._afterCommitSize
    _afterRollbackList.truncate(level._afterRollbackSize)
  }

  /** Returns the discarded `afterCommitList` entries, or null if none. */
  protected def resetCallbacks(): Array[Status => Unit] = {
    if (!_callbacksPresent) null else resetCallbacksImpl()
  }

  private def resetCallbacksImpl(): Array[Status => Unit] = {
    _beforeCommitList.size = 0
    _whileValidatingList.size = 0
    _whilePreparingList.size = 0
    _whileCommittingList.size = 0
    _afterRollbackList.size = 0
    _afterCommitList.truncate(0)
  }

  protected def fireWhileValidating() {
    val n = _whileValidatingList.size
    if (n > 0)
      fireWhileValidating(n - 1, internalCurrentLevel)
  }

  @tailrec private def fireWhileValidating(i: Int, level: AbstractNestingLevel) {
    if (i >= 0) {
      if (i < level._whileValidatingSize)
        fireWhileValidating(i, level.parLevel)
      else if (level.status.isInstanceOf[Txn.RolledBack])
        fireWhileValidating(level._whileValidatingSize - 1, level.parLevel) // skip the rest at this level
      else {
        try {
          _whileValidatingList(i)(level)
        } catch {
          case x: Throwable => level.requestRollback(UncaughtExceptionCause(x))
        }
        fireWhileValidating(i - 1, level)
      }
    }
  }

  //////////// implementation of functionality for the InTxn user

  override def rootLevel: AbstractNestingLevel = internalCurrentLevel.root

  def beforeCommit(handler: InTxn => Unit) {
    requireActive()
    _callbacksPresent = true
    _beforeCommitList += handler
  }

  def whileValidating(handler: NestingLevel => Unit) {
    requireActive()
    _callbacksPresent = true
    _whileValidatingList += handler
  }

  def whilePreparing(handler: InTxnEnd => Unit) {
    requireNotDecided()
    _callbacksPresent = true
    _whilePreparingList += handler
  }

  def whileCommitting(handler: InTxnEnd => Unit) {
    requireNotCompleted()
    _callbacksPresent = true
    _whileCommittingList += handler
  }

  def afterCommit(handler: Status => Unit) {
    requireNotCompleted()
    _callbacksPresent = true
    _afterCommitList += handler
  }

  def afterRollback(handler: Status => Unit) {
    try {
      requireNotCompleted()
    } catch {
      case RollbackError => {
        handler(internalCurrentLevel.status)
        throw RollbackError
      }
    }
    _callbacksPresent = true
    _afterRollbackList += handler
  }

  def afterCompletion(handler: Status => Unit) {
    afterRollback(handler)
    _afterCommitList += handler
  }

  def setExternalDecider(decider: ExternalDecider) {
    if (status.decided)
      throw new IllegalArgumentException("can't set ExternalDecider after decision, status = " + status)

    if (_decider != null) {
      if (_decider != decider)
        throw new IllegalArgumentException("can't set two different ExternalDecider-s in the same top-level atomic block")
    } else {
      _decider = decider
      // the decider should be unregistered after either rollback or commit
      afterCompletion { status =>
        assert(_decider eq decider)
        _decider = null
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy