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

scala.concurrent.stm.ccstm.CommitBarrierImpl.scala Maven / Gradle / Ivy

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

package scala.concurrent.stm
package ccstm

import java.util.concurrent.TimeUnit

private[ccstm] object CommitBarrierImpl {
  object MemberCancelException extends Exception

  sealed abstract class State
  case object Active extends State
  case object MemberWaiting extends State
  case class Cancelled(cause: CommitBarrier.CancelCause) extends State
  case object Committed extends State
}

private[ccstm] class CommitBarrierImpl(timeoutNanos: Long) extends CommitBarrier {
  import CommitBarrier._
  import CommitBarrierImpl._

  private val lock = new Object

  /** The number of non-cancelled members. */
  private var activeCount = 0

  /** The number of members pending a commit decision. */
  private var waitingCount = 0

  /** If non-empty, all present and future members have been cancelled. */
  private var groupState: State = Active

  class MemberImpl(var executor: TxnExecutor) extends Member with Txn.ExternalDecider {

    @volatile private var state: State = Active
    @volatile private var target: NestingLevel = null

    def commitBarrier = CommitBarrierImpl.this

    def atomic[Z](body: InTxn => Z): Either[CancelCause, Z] = {
      try {
        executor { implicit txn =>
          if (state != Active) {
            Txn.rollback(Txn.UncaughtExceptionCause(MemberCancelException))
          }
          Txn.setExternalDecider(this)
          txn.asInstanceOf[InTxnImpl].commitBarrier = commitBarrier
          target = NestingLevel.current
          Right(body(txn))
        }
      } catch {
        case x: Throwable => {
          state match {
            case Active => {
              // txn rolled back before involving external decider, all
              // members must be rolled back
              cancelAll(MemberUncaughtExceptionCause(x))
              throw x
            }
            case MemberWaiting => {
              // interrupt during ExternalDecider, all already cancelled
              assert(x.isInstanceOf[InterruptedException])
              assert(groupState.isInstanceOf[Cancelled])
              throw x
            }
            case Cancelled(cause) => {
              // barrier state already updated
              Left(cause)
            }
            case Committed => {
              // control flow exception as part of group commit, tricky!
              throw x
            }
          }
        }
      } finally {
        target = null
      }
    }

    private def markCancelled(cause: CancelCause, isLocal: Boolean) {
      val firstCause = groupState match {
        case c: Cancelled => c
        case _ => Cancelled(cause)
      }

      state match {
        case Active => {
          state = firstCause
          activeCount -= 1
          checkBarrierCommit_nl()
        }
        case MemberWaiting => {
          state = firstCause
          activeCount -= 1
          waitingCount -= 1
          if (!isLocal) {
            // this member should exit its external decider
            lock.notifyAll()
          }
        }
        case Cancelled(_) => {}
        case Committed => {
          throw new IllegalStateException("can't cancel member after commit")
        }
      }
    }

    private[CommitBarrierImpl] def cancelImpl(cause: CancelCause) {
      lock.synchronized {
        markCancelled(cause, false)
      }
      val t = target
      if (t != null) {
        t.requestRollback(Txn.UncaughtExceptionCause(MemberCancelException))
        t.asInstanceOf[TxnLevelImpl].awaitCompleted(null, "CommitBarrier cancel")
      }
    }

    def cancel(cause: UserCancel) {
      cancelImpl(cause)
    }

    def shouldCommit(implicit txn: InTxnEnd): Boolean = {
      var cause = lock.synchronized { shouldCommit_nl() }
      if (cause != null)
        txn.rollback(cause)
      true
    }

    private def shouldCommit_nl(): Txn.RollbackCause = {
      if (state == Active) {
        state = MemberWaiting
        waitingCount += 1
        checkBarrierCommit_nl()
      }

      var t0 = 0L

      (while (true) {
        // cancelImpl is a no-op if we're already cancelled
        groupState match {
          case Cancelled(cause) => markCancelled(cause, true)
          case Committed if state == MemberWaiting => {
            state = Committed
            return null
          }
          case _ => {}
        }

        state match {
          case Cancelled(_) => return Txn.UncaughtExceptionCause(MemberCancelException)
          case MemberWaiting => {}
          case _ => throw new Error("impossible state " + state)
        }

        // wait
        try {
          if (timeoutNanos < Long.MaxValue) {
            val now = System.nanoTime()
            if (t0 == 0) {
              t0 = now
            }

            val remaining = (t0 + timeoutNanos) - now
            if (remaining <= 0) {
              cancelAll(Timeout)
              markCancelled(Timeout, true)
              return Txn.UncaughtExceptionCause(MemberCancelException)
            } else {
              val millis = TimeUnit.NANOSECONDS.toMillis(remaining)
              val nanos = remaining - TimeUnit.MILLISECONDS.toNanos(millis)
              lock.wait(millis, nanos.asInstanceOf[Int])
            }
          } else {
            lock.wait()
          }
        } catch {
          case x: InterruptedException => {
            // don't cancel ourself so we can throw InterruptedException
            cancelAll(MemberUncaughtExceptionCause(x))
            return Txn.UncaughtExceptionCause(x)
          }
        }
      }).asInstanceOf[Nothing]
    }
  }

  private def checkBarrierCommit_nl() {
    groupState match {
      case Active => {
        if (activeCount == waitingCount && activeCount > 0) {
          groupState = Committed
          lock.notifyAll()
        }
      }
      case _ => {}
    }
  }

  private[ccstm] def cancelAll(cause: CancelCause) {
    lock.synchronized {
      groupState match {
        case Active => {
          groupState = Cancelled(cause)
          lock.notifyAll()
        }
        case Cancelled(_) => {}
        case _ => throw new Error("impossible groupState " + groupState)
      }
    }
  }

  def addMember(cancelOnLocalRollback: Boolean)(implicit txn: MaybeTxn): Member = {
    lock.synchronized {
      groupState match {
        case Cancelled(_) => throw new IllegalStateException("commit barrier has already rolled back")
        case Committed => throw new IllegalStateException("commit barrier has already committed")
        case _ => activeCount += 1
      }
    }

    val m = new MemberImpl(atomic)

    if (cancelOnLocalRollback) {
      for (txn <- Txn.findCurrent)
        Txn.afterRollback({ _ => m.cancelImpl(CreatingTxnRolledBack) })(txn)
    }

    m
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy