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

scala.actors.Scheduler.scala Maven / Gradle / Ivy

/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2005-2007, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |                                         **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: Scheduler.scala 10369 2007-03-16 15:25:00Z phaller $


package scala.actors

import compat.Platform

import java.lang.{Runnable, Thread, InterruptedException}

import scala.collection.Set
import scala.collection.mutable.{ArrayBuffer, Buffer, HashMap, Queue, Stack, HashSet}

/**
 * The Scheduler object is used by
 * Actor to execute tasks of an execution of an actor.
 *
 * @version 0.9.5
 * @author Philipp Haller
 */
object Scheduler {
  private var sched: IScheduler =
    {
      var s: IScheduler = new FJTaskScheduler2
      s.start()
      s
    }

  def impl = sched
  def impl_= (scheduler: IScheduler) = {
    sched = scheduler
  }

  var tasks: LinkedQueue = null

  def snapshot(): unit = synchronized {
    tasks = sched.snapshot()
    sched.shutdown()
  }

  def restart(): unit = synchronized {
    sched = {
      var s: IScheduler = new FJTaskScheduler2
      s.start()
      s
    }
    while (!tasks.isEmpty()) {
      sched.execute(tasks.take().asInstanceOf[FJTask])
    }
    tasks = null
  }

  def start(task: Reaction) = sched.start(task)
  def execute(task: Reaction) = {
    val t = currentThread
    if (t.isInstanceOf[FJTaskRunner]) {
      val tr = t.asInstanceOf[FJTaskRunner]
      tr.push(new FJTask {
        def run() {
          task.run()
        }
      })
    } else sched.execute(task)
  }

  def tick(a: Actor) = sched.tick(a)
  def terminated(a: Actor) = sched.terminated(a)
  def pendReaction: unit = sched.pendReaction
  def unPendReaction: unit = sched.unPendReaction

  def shutdown() = sched.shutdown()

  def onLockup(handler: () => unit) = sched.onLockup(handler)
  def onLockup(millis: int)(handler: () => unit) = sched.onLockup(millis)(handler)
  def printActorDump = sched.printActorDump
}


/**
 * This abstract class provides a common interface for all
 * schedulers used to execute actor tasks.
 *
 * @version 0.9.5
 * @author Philipp Haller
 */
trait IScheduler {
  def start(): unit

  def start(task: Reaction): unit
  def execute(task: Reaction): unit
  def execute(task: FJTask): unit

  def getTask(worker: WorkerThread): Runnable
  def tick(a: Actor): unit
  def terminated(a: Actor): unit
  def pendReaction: unit
  def unPendReaction: unit

  def snapshot(): LinkedQueue
  def shutdown(): unit

  def onLockup(handler: () => unit): unit
  def onLockup(millis: int)(handler: () => unit): unit
  def printActorDump: unit

  val QUIT_TASK = new Reaction(null) {
    override def run(): unit = {}
    override def toString() = "QUIT_TASK"
  }
}


/**
 * This scheduler executes the tasks of an actor on a single
 * thread (the current thread).
 *
 * @version 0.9.5
 * @author Philipp Haller
 */
class SingleThreadedScheduler extends IScheduler {
  def start() {}

  def start(task: Reaction) {
    // execute task immediately on same thread
    task.run()
  }

  def execute(task: Reaction) {
    // execute task immediately on same thread
    task.run()
  }

  def execute(task: FJTask) {
    // execute task immediately on same thread
    task.run()
  }

  def getTask(worker: WorkerThread): Runnable = null
  def tick(a: Actor): Unit = {}
  def terminated(a: Actor): unit = {}
  def pendReaction: unit = {}
  def unPendReaction: unit = {}

  def shutdown(): Unit = {}
  def snapshot(): LinkedQueue = { null }

  def onLockup(handler: () => unit): unit = {}
  def onLockup(millis: int)(handler: () => unit): unit = {}
  def printActorDump: unit = {}
}


/**
 * The QuickException class is used to manage control flow
 * of certain schedulers and worker threads.
 *
 * @version 0.9.4
 * @author Philipp Haller
 */
private[actors] class QuitException extends Throwable {
  /*
   For efficiency reasons we do not fill in
   the execution stack trace.
   */
  override def fillInStackTrace(): Throwable = this
}

/**
 * 

* The class WorkerThread is used by schedulers to execute * actor tasks on multiple threads. *

*

* !!ACHTUNG: If you change this, make sure you understand the following * proof of deadlock-freedom!! *

*

* We proof that there is no deadlock between the scheduler and * any worker thread possible. For this, note that the scheduler * only acquires the lock of a worker thread by calling * execute. This method is only called when the worker thread * is in the idle queue of the scheduler. On the other hand, a * worker thread only acquires the lock of the scheduler when it * calls getTask. At the only callsite of getTask, * the worker thread holds its own lock. *

*

* Thus, deadlock can only occur when a worker thread calls * getTask while it is in the idle queue of the scheduler, * because then the scheduler might call (at any time!) execute * which tries to acquire the lock of the worker thread. In such * a situation the worker thread would be waiting to acquire the * lock of the scheduler and vice versa. *

*

* Therefore, to prove deadlock-freedom, it suffices to ensure * that a worker thread will never call getTask when * it is in the idle queue of the scheduler. *

*

* A worker thread enters the idle queue of the scheduler when * getTask returns null. Then it will also stay * in the while-loop W (while (task eq null)) until * task becomes non-null. The only way this can happen is * through a call of execute by the scheduler. Before every * call of execute the worker thread is removed from the idle * queue of the scheduler. Only then--after executing its task-- * the worker thread may call getTask. However, the scheduler * is unable to call execute as the worker thread is not in * the idle queue any more. In fact, the scheduler made sure * that this is the case even _before_ calling execute and * thus releasing the worker thread from the while-loop W. Thus, * the property holds for every possible interleaving of thread * execution. QED *

* * @version 0.9.4 * @author Philipp Haller */ class WorkerThread(sched: IScheduler) extends Thread { private var task: Runnable = null private[actors] var running = true def execute(r: Runnable) = synchronized { task = r notify() } override def run(): unit = try { while (running) { if (task ne null) { try { task.run() } catch { case consumed: InterruptedException => if (!running) throw new QuitException } } this.synchronized { task = sched.getTask(this) while (task eq null) { try { wait() } catch { case consumed: InterruptedException => if (!running) throw new QuitException } } if (task == sched.QUIT_TASK) { running = false } } } } catch { case consumed: QuitException => // allow thread to quit } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy