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

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

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

package scala.concurrent.stm
package skel

import annotation.tailrec


private[stm] class CallbackList[A] {

  private final val InitialCapacity = 128
  private final val MaxEmptyCapacity = 8192

  private var _size = 0
  private var _data = new Array[A => Unit](InitialCapacity)

  def isEmpty: Boolean = _size == 0

  def size: Int = _size

  def size_= (newSize: Int) {
    if (newSize != _size)
      changeSize(newSize)
  }

  private def changeSize(newSize: Int) {
    if (newSize < 0 || newSize > _size)
      throw new IllegalArgumentException

    if (newSize == 0 && _data.length > MaxEmptyCapacity) {
      // reallocate if the array is too big, so that a single large txn doesn't
      // permanently increase the memory footprint of this thread
      reset()
    } else {
      java.util.Arrays.fill(_data.asInstanceOf[Array[AnyRef]], newSize, _size, null)
      _size = newSize
    }
  }

  private def reset() {
    _data = new Array[A => Unit](InitialCapacity)
    _size = 0
  }

  def += (handler: A => Unit) {
    if (_size == _data.length)
      grow
    _data(_size) = handler
    _size += 1
  }

  private def grow() {
    val a = new Array[A => Unit](_data.length * 2)
    System.arraycopy(_data, 0, a, 0, _data.length)
    _data = a
  }

  def apply(i: Int): (A => Unit) = _data(i)

  def fire(level: NestingLevel, arg: A) {
    if (_size > 0)
      fire(level, arg, 0)
  }

  @tailrec private def fire(level: NestingLevel, arg: A, i: Int) {
    if (i < _size && shouldFire(level)) {
      try {
        _data(i)(arg)
      } catch {
        case x: Throwable => {
          val s = level.requestRollback(Txn.UncaughtExceptionCause(x))
          assert(s.isInstanceOf[Txn.RolledBack])
        }
      }
      fire(level, arg, i + 1)
    }
  }

  private def shouldFire(level: NestingLevel): Boolean = !level.status.isInstanceOf[Txn.RolledBack]

  /** Sets the size of this callback list to `newSize`, and returns a the
   *  discarded handlers.
   */
  def truncate(newSize: Int): Array[A => Unit] = {
    if (_size == newSize) {
      // nothing to do
      null
    } else {
      // copy
      val z = new Array[A => Unit](_size - newSize)
      System.arraycopy(_data, newSize, z, 0, z.length)
      size = newSize
      z
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy