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

scala.actors.Actor.scala Maven / Gradle / Ivy

/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2005-2007, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: Actor.scala 10241 2007-03-08 10:06:20Z phaller $

package scala.actors

import scala.collection.mutable.{HashSet, Queue}
import scala.compat.Platform

/**
 * The Actor object provides functions for the definition of
 * actors, as well as all actor operations, such as
 * receive, react, reply,
 * etc.
 *
 * @version 0.9.4
 * @author Philipp Haller
 */
object Actor {

  private[actors] val tl = new ThreadLocal

  /**
   * Returns the currently executing actor. Should be used instead
   * of this in all blocks of code executed by
   * actors.
   *
   * @return returns the currently executing actor.
   */
  def self: Actor = synchronized {
    var a = tl.get.asInstanceOf[Actor]
    if (null eq a) {
      a = new ActorProxy(currentThread)
      tl.set(a)
    }
    a
  }

  /**
   * 

This function is used for the definition of actors.

*

The following example demonstrates its usage:

   * import scala.actors.Actor._
   * ...
   * val a = actor {
   *   ...
   * }
   * 
* * @param body the code block to be executed by the newly created actor * @return the newly created actor. Note that it is automatically started. */ def actor(body: => Unit): Actor = synchronized { val actor = new Actor { def act() = body } actor.start() actor } /** * Receives the next message from the mailbox of the current actor * self. */ def ? : Any = self.? /** * Receives a message from the mailbox of * self. Blocks if no message matching any of the * cases of f can be received. * * @param f a partial function specifying patterns and actions * @return the result of processing the received message */ def receive[a](f: PartialFunction[Any, a]): a = self.receive(f) /** * Receives a message from the mailbox of * self. Blocks at most msec * milliseconds if no message matching any of the cases of * f can be received. If no message could be * received the TIMEOUT action is executed if * specified. * * @param msec the time span before timeout * @param f a partial function specifying patterns and actions * @return the result of processing the received message */ def receiveWithin[R](msec: long)(f: PartialFunction[Any, R]): R = self.receiveWithin(msec)(f) /** * Lightweight variant of receive. * * Actions in f have to contain the rest of the * computation of self, as this method will never * return. * * @param f a partial function specifying patterns and actions * @return this function never returns */ def react(f: PartialFunction[Any, Unit]): Nothing = self.react(f) /** * Lightweight variant of receiveWithin. * * Actions in f have to contain the rest of the * computation of self, as this method will never * return. * * @param msec the time span before timeout * @param f a partial function specifying patterns and actions * @return this function never returns */ def reactWithin(msec: long)(f: PartialFunction[Any, Unit]): Nothing = self.reactWithin(msec)(f) def eventloop(f: PartialFunction[Any, Unit]): Nothing = self.react(new RecursiveProxyHandler(self, f)) private class RecursiveProxyHandler(a: Actor, f: PartialFunction[Any, Unit]) extends PartialFunction[Any, Unit] { def isDefinedAt(m: Any): boolean = true // events are immediately removed from the mailbox def apply(m: Any): Unit = { if (f.isDefinedAt(m)) f(m) self.react(this) } } /** * Returns the actor which sent the last received message. */ def sender: Actor = self.sender /** * Send msg to the actor waiting in a call to * !?. */ def reply(msg: Any): Unit = self.reply(msg) /** * Send () to the actor waiting in a call to * !?. */ def reply(): Unit = self.reply(()) private[actors] trait Body[a] { def andThen[b](other: => b): Nothing } implicit def mkBody[a](body: => a) = new Body[a] { def andThen[b](other: => b): Nothing = seq(body, other) } /** * Causes self to repeatedly execute * body. * * @param body the code block to be executed */ def loop(body: => Unit): Nothing = body andThen loop(body) /** * Causes self to execute first * followed by next. * * @param first the first code block to be executed * @param next the second code block to be executed */ def seq[a, b](first: => a, next: => b): Nothing = { val s = self val killNext = s.kill s.kill = () => { s.kill = killNext; next; exit('normal) } first exit('normal) } /** * Links self to actor to. * * @param to the actor to link to * @return */ def link(to: Actor): Actor = self.link(to) /** * Links self to actor defined by body. * * @param body ... * @return ... */ def link(body: => Unit): Actor = self.link(body) /** * Unlinks self from actor from. * * @param from the actor to unlink from */ def unlink(from: Actor): Unit = self.unlink(from) /** *

* Terminates execution of self with the following * effect on linked actors: *

*

* For each linked actor a with * trapExit set to true, send message * {'EXIT, self, reason} to a. *

*

* For each linked actor a with * trapExit set to false (default), * call a.exit(reason) if * reason != 'normal. *

*/ def exit(reason: AnyRef): Nothing = self.exit(reason) } /** *

* This class provides (together with Channel) an * implementation of event-based actors. *

*

* The main ideas of our approach are explained in the papers
* Event-Based Programming without Inversion of Control, * Philipp Haller and Martin Odersky, Proc. JMLC 2006 *

* Actors that Unify Threads and Events, * Philipp Haller and Martin Odersky, LAMP-REPORT-2007-001, EPFL *

* * @version 0.9.4 * @author Philipp Haller */ trait Actor extends OutputChannel[Any] { private var received: Option[Any] = None private[actors] val waitingForNone = (m: Any) => false private[actors] var waitingFor: Any => boolean = waitingForNone private[actors] var isSuspended = false private val mailbox = new MessageQueue private var sessions: List[Channel[Any]] = Nil private def send(msg: Any, session: Channel[Any]) = synchronized { tick() if (waitingFor(msg)) { received = Some(msg) sessions = session :: sessions waitingFor = waitingForNone if (timeoutPending) { timeoutPending = false TimerThread.trashRequest(this) } if (isSuspended) resumeActor() else scheduleActor(null, msg) } else { mailbox.append(msg, session) } } def receive[R](f: PartialFunction[Any, R]): R = { assert(Actor.self == this, "receive from channel belonging to other actor") if (shouldExit) exit() // links this.synchronized { tick() val qel = mailbox.extractFirst((m: Any) => f.isDefinedAt(m)) if (null eq qel) { waitingFor = f.isDefinedAt isSuspended = true suspendActor() } else { received = Some(qel.msg) sessions = qel.session :: sessions } waitingFor = waitingForNone isSuspended = false } val result = f(received.get) sessions = sessions.tail result } def receiveWithin[R](msec: long)(f: PartialFunction[Any, R]): R = { assert(Actor.self == this, "receive from channel belonging to other actor") if (shouldExit) exit() // links this.synchronized { tick() // first, remove spurious TIMEOUT message from mailbox if any val spurious = mailbox.extractFirst((m: Any) => m == TIMEOUT) val qel = mailbox.extractFirst((m: Any) => f.isDefinedAt(m)) if (null eq qel) { if (msec == 0) { if (f.isDefinedAt(TIMEOUT)) return f(TIMEOUT) else error("unhandled timeout") } else { waitingFor = f.isDefinedAt isSuspended = true received = None suspendActorFor(msec) if (received.isEmpty) { if (f.isDefinedAt(TIMEOUT)) { waitingFor = waitingForNone isSuspended = false val result = f(TIMEOUT) return result } else error("unhandled timeout") } } } else { received = Some(qel.msg) sessions = qel.session :: sessions } waitingFor = waitingForNone isSuspended = false } val result = f(received.get) sessions = sessions.tail result } def react(f: PartialFunction[Any, Unit]): Nothing = { assert(Actor.self == this, "react on channel belonging to other actor") if (shouldExit) exit() // links Scheduler.pendReaction this.synchronized { tick() val qel = mailbox.extractFirst((m: Any) => f.isDefinedAt(m)) if (null eq qel) { waitingFor = f.isDefinedAt continuation = f isDetached = true } else { sessions = qel.session :: sessions scheduleActor(f, qel.msg) } throw new SuspendActorException } } def reactWithin(msec: long)(f: PartialFunction[Any, Unit]): Nothing = { assert(Actor.self == this, "react on channel belonging to other actor") if (shouldExit) exit() // links Scheduler.pendReaction this.synchronized { tick() // first, remove spurious TIMEOUT message from mailbox if any val spurious = mailbox.extractFirst((m: Any) => m == TIMEOUT) val qel = mailbox.extractFirst((m: Any) => f.isDefinedAt(m)) if (null eq qel) { waitingFor = f.isDefinedAt TimerThread.requestTimeout(this, f, msec) timeoutPending = true continuation = f isDetached = true } else { sessions = qel.session :: sessions scheduleActor(f, qel.msg) } throw new SuspendActorException } } /** * The behavior of an actor is specified by implementing this * abstract method. Note that the preferred way to create actors * is through the actor method * defined in object Actor. */ def act(): Unit /** * Sends msg to this actor (asynchronous). */ def !(msg: Any): Unit = { send(msg, Actor.self.getReplyChannel) } /** * Forwards msg to this actor (asynchronous). */ def forward(msg: Any): Unit = send(msg, Actor.sender.getReplyChannel) /** * Sends msg to this actor and awaits reply * (synchronous). */ def !?(msg: Any): Any = { val replyChannel = Actor.self.freshReply() this ! msg replyChannel.receive { case x => x } } /** * Sends msg to this actor and awaits reply * (synchronous) within msec milliseconds. * When the timeout occurs, None is returned. * Otherwise, returns Some(value) where * value is the reply value. */ def !?(msec: long, msg: Any): Option[Any] = { val replyChannel = Actor.self.freshReply() this ! msg replyChannel.receiveWithin(msec) { case TIMEOUT => None case x => Some(x) } } /** * Sends msg to this actor and immediately * returns a future representing the reply value. */ def !!(msg: Any): Future[Any] = { val ftch = new Channel[Any](Actor.self) send(msg, ftch) new Future[Any](ftch) { def apply() = if (isSet) value.get else ch.receive { case any => value = Some(any); any } def isSet = value match { case None => ch.receiveWithin(0) { case TIMEOUT => false case any => value = Some(any); true } case Some(_) => true } } } /** * Sends msg to this actor and immediately * returns a future representing the reply value. * The reply is post-processed using the partial function * f. This also allows to recover a more * precise type for the reply value. */ def !![a](msg: Any, f: PartialFunction[Any, a]): Future[a] = { val ftch = new Channel[Any](Actor.self) send(msg, ftch) new Future[a](ftch) { def apply() = if (isSet) value.get else ch.receive { case any => value = Some(f(any)); value.get } def isSet = value match { case None => ch.receiveWithin(0) { case TIMEOUT => false case any => value = Some(f(any)); true } case Some(_) => true } } } /** * Replies with msg to the sender waiting in * a synchronous send. */ def reply(msg: Any): Unit = session ! msg private var rc = new Channel[Any](this) def getReplyChannel = rc def freshReply() = { rc = new Channel[Any]; rc } /** * Receives the next message from this actor's mailbox. */ def ? : Any = receive { case x => x } private[actors] def sender: Actor = if (sessions.isEmpty) null else sessions.head.asInstanceOf[Channel[Any]].receiver private[actors] def session: Channel[Any] = if (sessions.isEmpty) null else sessions.head.asInstanceOf[Channel[Any]] private[actors] var continuation: PartialFunction[Any, Unit] = null private[actors] var timeoutPending = false private[actors] var isDetached = false private[actors] var isWaiting = false // guarded by lock of this private[actors] def scheduleActor(f: PartialFunction[Any, Unit], msg: Any) = if ((f eq null) && (continuation eq null)) { // do nothing (timeout is handled instead) } else { val task = new Reaction(this, if (f eq null) continuation else f, msg) Scheduler execute task } private[actors] def tick(): Unit = Scheduler tick this private[actors] var kill = () => {} def suspendActor() { isWaiting = true while(isWaiting) { try { wait() } catch { case _: InterruptedException => } } // links: check if we should exit if (shouldExit) exit() } def suspendActorFor(msec: long) { val ts = Platform.currentTime var waittime = msec var fromExc = false isWaiting = true while(isWaiting) { try { fromExc = false wait(waittime) } catch { case _: InterruptedException => { fromExc = true val now = Platform.currentTime val waited = now-ts waittime = msec-waited if (waittime < 0) { isWaiting = false } } } if (!fromExc) { isWaiting = false } } // links: check if we should exit if (shouldExit) exit() } def resumeActor() { isWaiting = false notify() } /** * Starts this actor. */ def start() { Scheduler start new Reaction(this) } private[actors] var links: List[Actor] = Nil /** * Links self to actor to. * * @param to ... * @return ... */ def link(to: Actor): Actor = { links = to :: links to.linkTo(this) to } /** * Links self to actor defined by body. */ def link(body: => Unit): Actor = { val actor = new Actor { def act() = body } link(actor) actor.start() actor } private[actors] def linkTo(to: Actor) { links = to :: links } /** Unlinks self from actor from. */ def unlink(from: Actor) { links = links.remove(from.==) from.unlinkFrom(this) } private[actors] def unlinkFrom(from: Actor) { links = links.remove(from.==) } var trapExit = false private[actors] var exitReason: AnyRef = 'normal private[actors] var exiting = false private[actors] var shouldExit = false /** *

* Terminates execution of self with the following * effect on linked actors: *

*

* For each linked actor a with * trapExit set to true, send message * {'EXIT, self, reason} to a. *

*

* For each linked actor a with * trapExit set to false (default), * call a.exit(reason) if * reason != 'normal. *

*/ def exit(reason: AnyRef): Nothing = { if (reason == 'normal) kill() // links if (!links.isEmpty) { exitReason = reason exitLinked() } throw new ExitActorException } /** * Terminates with exit reason 'normal. */ def exit(): Nothing = exit('normal) // Assume !links.isEmpty private[actors] def exitLinked() { exiting = true // remove this from links links = links.remove(this.==) // exit linked processes links.foreach((linked: Actor) => { unlink(linked) if (!linked.exiting) linked.exit(this, exitReason) }) } // Assume !links.isEmpty private[actors] def exitLinked(reason: AnyRef) { exitReason = reason exiting = true // remove this from links links = links.remove(this.==) // exit linked processes links.foreach((linked: Actor) => { unlink(linked) if (!linked.exiting) linked.exit(this, exitReason) }) } // Assume !this.exiting private[actors] def exit(from: Actor, reason: AnyRef) { if (trapExit) { this ! Triple('EXIT, from, reason) } else if (reason != 'normal) this.synchronized { shouldExit = true exitReason = reason if (isSuspended) resumeActor() else if (isDetached) scheduleActor(null, null) } } } /**

* This object is used as the timeout pattern in * * receiveWithin and * * reactWithin. *

*

* The following example demonstrates its usage: *

 *    receiveWithin(500) {
 *      case (x, y) => ...
 *      case TIMEOUT => ...
 *    }
* * @version 0.9.4 * @author Philipp Haller */ case object TIMEOUT /**

* This class is used to manage control flow of actor * executions. *

* * @version 0.9.4 * @author Philipp Haller */ private[actors] class SuspendActorException extends Throwable { /* * For efficiency reasons we do not fill in * the execution stack trace. */ override def fillInStackTrace(): Throwable = this }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy