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

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

The newest version!
/* scala-stm - (c) 2009-2012, Stanford University, PPL */

package scala.concurrent.stm.ccstm

import annotation.tailrec
import java.util.concurrent.TimeUnit

/** A retry set representation. */
private[ccstm] class RetrySet(val size: Int,
                              handles: Array[Handle[_]],
                              versions: Array[CCSTM.Version]) {
  import CCSTM._

  /** Returns the number of nanoseconds of blocking that was performed. */
  @throws(classOf[InterruptedException])
  def awaitRetry(timeoutNanos: Long): Long = {
    if (size == 0 && timeoutNanos == Long.MaxValue)
      throw new IllegalStateException("explicit retries cannot succeed because cumulative read set is empty")

    val begin = System.nanoTime

    val d = begin + timeoutNanos
    val deadline = if (d < 0) Long.MaxValue else d // handle arithmetic overflow

    val timeoutExceeded = !attemptAwait(deadline)

    val actualElapsed = System.nanoTime - begin

    if (Stats.top != null) {
      Stats.top.retrySet += size
      val millis = TimeUnit.NANOSECONDS.toMillis(actualElapsed)
      Stats.top.retryWaitElapsed += millis.asInstanceOf[Int]
    }

    // to cause the proper retryFor to wake up we need to present an illusion
    // that we didn't block for too long
    //
    //  reportedElapsed = min(now, deadline) - begin
    //  reportedElapsed = min(now - begin, deadline - begin)
    math.min(actualElapsed, deadline - begin)
  }

  /** Returns true if something changed, false if the deadline was reached. */
  @throws(classOf[InterruptedException])
  private def attemptAwait(nanoDeadline: Long): Boolean = {
    // Spin a few times, counting one spin per read set element
    var spins = 0
    while (size > 0 && spins < SpinCount + YieldCount) {
      if (changed)
        return true
      spins += size
      if (spins > SpinCount) {
        Thread.`yield`
        if (nanoDeadline != Long.MaxValue && System.nanoTime > nanoDeadline)
          return false
      }
    }

    return blockingAttemptAwait(nanoDeadline)
  }

  @throws(classOf[InterruptedException])
  @tailrec
  private def blockingAttemptAwait(nanoDeadline: Long,
                                   event: WakeupManager.Event = wakeupManager.subscribe,
                                   i: Int = size - 1): Boolean = {
    if (i < 0) {
      // event has been completed, time to block
      if (!event.tryAwaitUntil(nanoDeadline))
        false // timed out
      else
        changed || blockingAttemptAwait(nanoDeadline) // event fired
    } else {
      // still building the event
      val h = handles(i)
      if (!event.addSource(h))
        changed || blockingAttemptAwait(nanoDeadline) // event fired
      else if (!addPendingWakeup(h, versions(i)))
        true // direct evidence of change
      else
        blockingAttemptAwait(nanoDeadline, event, i - 1) // keep building
    }
  }

  @tailrec
  private def addPendingWakeup(handle: Handle[_], ver: CCSTM.Version): Boolean = {
    val m = handle.meta
    if (changing(m) || version(m) != ver)
      false // handle has already changed
    else if (pendingWakeups(m) || handle.metaCAS(m, withPendingWakeups(m)))
      true // already has pending wakeup, or CAS to add it was successful
    else
      addPendingWakeup(handle, ver) // try again
  }

  private def changed: Boolean = {
    var i = size - 1
    while (i >= 0) {
      val m = handles(i).meta
      if (changing(m) || version(m) != versions(i))
        return true
      i -= 1
    }
    return false
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy