From b5f9991ac901564b0fe0d01db14239c13f9d54f2 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 14 Feb 2011 02:34:40 +0100 Subject: [PATCH 1/8] Possible optimization for EBEDD --- .../ExecutorBasedEventDrivenDispatcher.scala | 62 +++++++++---------- ...sedEventDrivenWorkStealingDispatcher.scala | 8 +-- .../scala/akka/dispatch/MailboxHandling.scala | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 4e48806a8c..1c0a9bdc84 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -9,7 +9,7 @@ import akka.util.{ReflectiveAccess, Switch} import java.util.Queue import java.util.concurrent.atomic.AtomicReference -import java.util.concurrent.{ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue, LinkedBlockingQueue} +import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue, LinkedBlockingQueue} /** * Default settings are: @@ -128,7 +128,7 @@ class ExecutorBasedEventDrivenDispatcher( private[akka] def registerForExecution(mbox: MessageQueue with ExecutableMailbox): Unit = if (active.isOn) { - if (mbox.suspended.isOff && mbox.dispatcherLock.tryLock()) { + if (!mbox.suspended.locked && mbox.dispatcherLock.tryLock()) { try { executorService.get() execute mbox } catch { @@ -143,14 +143,14 @@ class ExecutorBasedEventDrivenDispatcher( def suspend(actorRef: ActorRef) { log.slf4j.debug("Suspending {}",actorRef.uuid) - getMailbox(actorRef).suspended.switchOn + getMailbox(actorRef).suspended.tryLock } def resume(actorRef: ActorRef) { log.slf4j.debug("Resuming {}",actorRef.uuid) val mbox = getMailbox(actorRef) - mbox.suspended.switchOff - registerForExecution(mbox) + if (mbox.suspended.tryUnlock) + registerForExecution(mbox) } } @@ -162,12 +162,12 @@ trait ExecutableMailbox extends Runnable { self: MessageQueue => def dispatcher: ExecutorBasedEventDrivenDispatcher final def run = { - val reschedule = try { + try { try { processMailbox() } catch { case ie: InterruptedException => true } } finally { dispatcherLock.unlock() } - if (reschedule || !self.isEmpty) + if (!self.isEmpty) dispatcher.registerForExecution(this) } @@ -176,33 +176,33 @@ trait ExecutableMailbox extends Runnable { self: MessageQueue => * * @return true if the processing finished before the mailbox was empty, due to the throughput constraint */ - final def processMailbox(): Boolean = { - if (self.suspended.isOn) - true - else { + final def processMailbox() { + if (!self.suspended.locked) { var nextMessage = self.dequeue - if (nextMessage ne null) { - val throttle = dispatcher.throughput > 0 - var processedMessages = 0 - val isDeadlineEnabled = throttle && dispatcher.throughputDeadlineTime > 0 - val started = if (isDeadlineEnabled) System.currentTimeMillis else 0 - do { - nextMessage.invoke + if (nextMessage ne null) { //If we have a message + if (dispatcher.throughput <= 1) //If we only run one message per process + nextMessage.invoke //Just run it + else { //But otherwise, if we are throttled, we need to do some book-keeping + var processedMessages = 0 + val isDeadlineEnabled = dispatcher.throughputDeadlineTime > 0 + val deadlineNs = if (isDeadlineEnabled) System.nanoTime + TimeUnit.MILLISECONDS.toNanos(dispatcher.throughputDeadlineTime) else 0 + do { + nextMessage.invoke - if (throttle) { // Will be elided when false - processedMessages += 1 - if ((processedMessages >= dispatcher.throughput) || - (isDeadlineEnabled && (System.currentTimeMillis - started) >= dispatcher.throughputDeadlineTime)) // If we're throttled, break out - return !self.isEmpty - } - - if (self.suspended.isOn) - return true - - nextMessage = self.dequeue - } while (nextMessage ne null) + nextMessage = + if (self.suspended.locked) { + null //If we are suspended, abort + } + else { //If we aren't suspended, we need to make sure we're not overstepping our boundraries + processedMessages += 1 + if ((processedMessages >= dispatcher.throughput) || (isDeadlineEnabled && System.nanoTime >= deadlineNs)) // If we're throttled, break out + null //We reached our boundraries, abort + else + self.dequeue //Dequeue the next message + } + } while (nextMessage ne null) + } } - false } } } diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala index 3d4a6c439b..54aec2607d 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala @@ -95,13 +95,13 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher( * @return */ private def processMailbox(mailbox: MessageQueue): Boolean = try { - if (mailbox.suspended.isOn) + if (mailbox.suspended.locked) return false var messageInvocation = mailbox.dequeue while (messageInvocation ne null) { messageInvocation.invoke - if (mailbox.suspended.isOn) + if (mailbox.suspended.locked) return false messageInvocation = mailbox.dequeue } @@ -180,12 +180,12 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher( def suspend(actorRef: ActorRef) { - getMailbox(actorRef).suspended.switchOn + getMailbox(actorRef).suspended.tryLock } def resume(actorRef: ActorRef) { val mbox = getMailbox(actorRef) - mbox.suspended.switchOff + mbox.suspended.tryUnlock executorService.get() execute mbox } diff --git a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala index 9b364b3af1..68e8cf68ce 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MailboxHandling.scala @@ -19,7 +19,7 @@ class MessageQueueAppendFailedException(message: String) extends AkkaException(m */ trait MessageQueue { val dispatcherLock = new SimpleLock - val suspended = new Switch(false) + val suspended = new SimpleLock def enqueue(handle: MessageInvocation) def dequeue(): MessageInvocation def size: Int From 5cae50d3bfbb3d583113d4fb1d0af2405ef4aae6 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 14 Feb 2011 02:40:57 +0100 Subject: [PATCH 2/8] Removing conditional scheduling --- .../akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 1c0a9bdc84..84f9d7b04c 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -149,8 +149,8 @@ class ExecutorBasedEventDrivenDispatcher( def resume(actorRef: ActorRef) { log.slf4j.debug("Resuming {}",actorRef.uuid) val mbox = getMailbox(actorRef) - if (mbox.suspended.tryUnlock) - registerForExecution(mbox) + mbox.suspended.tryUnlock + registerForExecution(mbox) } } From 0d75541a4c5a703ce9b6cf37173bb73ca196fdb0 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 14 Feb 2011 02:51:26 +0100 Subject: [PATCH 3/8] Spellchecking and elided a try-block --- .../dispatch/ExecutorBasedEventDrivenDispatcher.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala index 84f9d7b04c..2fa16eca71 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala @@ -163,7 +163,9 @@ trait ExecutableMailbox extends Runnable { self: MessageQueue => final def run = { try { - try { processMailbox() } catch { case ie: InterruptedException => true } + processMailbox() + } catch { + case ie: InterruptedException => } finally { dispatcherLock.unlock() } @@ -193,10 +195,10 @@ trait ExecutableMailbox extends Runnable { self: MessageQueue => if (self.suspended.locked) { null //If we are suspended, abort } - else { //If we aren't suspended, we need to make sure we're not overstepping our boundraries + else { //If we aren't suspended, we need to make sure we're not overstepping our boundaries processedMessages += 1 if ((processedMessages >= dispatcher.throughput) || (isDeadlineEnabled && System.nanoTime >= deadlineNs)) // If we're throttled, break out - null //We reached our boundraries, abort + null //We reached our boundaries, abort else self.dequeue //Dequeue the next message } From a17e9c271f984d5d9387c60f268221e14d34604b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 15 Feb 2011 00:37:53 +0100 Subject: [PATCH 4/8] Lowering overhead for receiving messages --- .../src/main/scala/akka/actor/Actor.scala | 78 +++++++++---------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 607f54204b..56153bf94e 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -23,7 +23,10 @@ import akka.japi. {Creator, Procedure} */ @serializable sealed trait LifeCycleMessage -case class HotSwap(code: ActorRef => Actor.Receive, discardOld: Boolean = true) extends LifeCycleMessage { +/* Marker trait to show which Messages are automatically handled by Akka */ +sealed trait AutoReceivedMessage { self: LifeCycleMessage => } + +case class HotSwap(code: ActorRef => Actor.Receive, discardOld: Boolean = true) extends AutoReceivedMessage with LifeCycleMessage { /** * Java API */ @@ -40,22 +43,22 @@ case class HotSwap(code: ActorRef => Actor.Receive, discardOld: Boolean = true) def this(code: akka.japi.Function[ActorRef,Procedure[Any]]) = this(code, true) } -case object RevertHotSwap extends LifeCycleMessage +case object RevertHotSwap extends AutoReceivedMessage with LifeCycleMessage -case class Restart(reason: Throwable) extends LifeCycleMessage +case class Restart(reason: Throwable) extends AutoReceivedMessage with LifeCycleMessage -case class Exit(dead: ActorRef, killer: Throwable) extends LifeCycleMessage +case class Exit(dead: ActorRef, killer: Throwable) extends AutoReceivedMessage with LifeCycleMessage -case class Link(child: ActorRef) extends LifeCycleMessage +case class Link(child: ActorRef) extends AutoReceivedMessage with LifeCycleMessage -case class Unlink(child: ActorRef) extends LifeCycleMessage +case class Unlink(child: ActorRef) extends AutoReceivedMessage with LifeCycleMessage -case class UnlinkAndStop(child: ActorRef) extends LifeCycleMessage +case class UnlinkAndStop(child: ActorRef) extends AutoReceivedMessage with LifeCycleMessage + +case object PoisonPill extends AutoReceivedMessage with LifeCycleMessage case object ReceiveTimeout extends LifeCycleMessage -case object PoisonPill extends LifeCycleMessage - case class MaximumNumberOfRestartsWithinTimeRangeReached( @BeanProperty val victim: ActorRef, @BeanProperty val maxNrOfRetries: Option[Int], @@ -303,8 +306,7 @@ trait Actor extends Logging { "\n\t\t'val actor = Actor.actorOf[MyActor]', or" + "\n\t\t'val actor = Actor.actorOf(new MyActor(..))', or" + "\n\t\t'val actor = Actor.actor { case msg => .. } }'") - val ref = optRef.asInstanceOf[Some[ActorRef]].get - ref.id = getClass.getName //FIXME: Is this needed? + optRef.asInstanceOf[Some[ActorRef]].get.id = getClass.getName //FIXME: Is this needed? optRef.asInstanceOf[Some[ActorRef]] } @@ -434,24 +436,30 @@ trait Actor extends Logging { private[akka] def apply(msg: Any) = fullBehavior(msg) + private final def autoReceiveMessage(msg: AutoReceivedMessage) { + msg match { + case HotSwap(code,discardOld) => become(code(self),discardOld) + case RevertHotSwap => unbecome + case Exit(dead, reason) => self.handleTrapExit(dead, reason) + case Link(child) => self.link(child) + case Unlink(child) => self.unlink(child) + case UnlinkAndStop(child) => self.unlink(child); child.stop + case Restart(reason) => throw reason + case PoisonPill => if(self.senderFuture.isDefined) { + self.senderFuture.get.completeWithException( + new ActorKilledException("PoisonPill") + ) + } + self.stop + } + } + /*Processingbehavior and fullBehavior are duplicates so make sure changes are done to both */ private lazy val processingBehavior: Receive = { - lazy val defaultBehavior = receive + val defaultBehavior = receive val actorBehavior: Receive = { - case HotSwap(code,discardOld) => become(code(self),discardOld) - case RevertHotSwap => unbecome - case Exit(dead, reason) => self.handleTrapExit(dead, reason) - case Link(child) => self.link(child) - case Unlink(child) => self.unlink(child) - case UnlinkAndStop(child) => self.unlink(child); child.stop - case Restart(reason) => throw reason - case PoisonPill => if(self.senderFuture.isDefined) { - self.senderFuture.get.completeWithException( - new ActorKilledException("PoisonPill") - ) - } - self.stop - case msg if !self.hotswap.isEmpty && + case l: AutoReceivedMessage => autoReceiveMessage(l) + case msg if self.hotswap.nonEmpty && self.hotswap.head.isDefinedAt(msg) => self.hotswap.head.apply(msg) case msg if self.hotswap.isEmpty && defaultBehavior.isDefinedAt(msg) => defaultBehavior.apply(msg) @@ -460,22 +468,10 @@ trait Actor extends Logging { } private lazy val fullBehavior: Receive = { - lazy val defaultBehavior = receive + val defaultBehavior = receive val actorBehavior: Receive = { - case HotSwap(code, discardOld) => become(code(self), discardOld) - case RevertHotSwap => unbecome - case Exit(dead, reason) => self.handleTrapExit(dead, reason) - case Link(child) => self.link(child) - case Unlink(child) => self.unlink(child) - case UnlinkAndStop(child) => self.unlink(child); child.stop - case Restart(reason) => throw reason - case PoisonPill => if(self.senderFuture.isDefined) { - self.senderFuture.get.completeWithException( - new ActorKilledException("PoisonPill") - ) - } - self.stop - case msg if !self.hotswap.isEmpty && + case l: AutoReceivedMessage => autoReceiveMessage(l) + case msg if self.hotswap.nonEmpty && self.hotswap.head.isDefinedAt(msg) => self.hotswap.head.apply(msg) case msg if self.hotswap.isEmpty && defaultBehavior.isDefinedAt(msg) => defaultBehavior.apply(msg) From 77a406dde735a9122c2c025195432925dc56e714 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 15 Feb 2011 01:28:29 +0100 Subject: [PATCH 5/8] Manual inlining and indentation --- .../src/main/scala/akka/actor/Actor.scala | 4 +-- .../src/main/scala/akka/actor/ActorRef.scala | 27 +++++++------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 56153bf94e..ce2d416aca 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -458,7 +458,7 @@ trait Actor extends Logging { private lazy val processingBehavior: Receive = { val defaultBehavior = receive val actorBehavior: Receive = { - case l: AutoReceivedMessage => autoReceiveMessage(l) + case l: AutoReceivedMessage => autoReceiveMessage(l) case msg if self.hotswap.nonEmpty && self.hotswap.head.isDefinedAt(msg) => self.hotswap.head.apply(msg) case msg if self.hotswap.isEmpty && @@ -470,7 +470,7 @@ trait Actor extends Logging { private lazy val fullBehavior: Receive = { val defaultBehavior = receive val actorBehavior: Receive = { - case l: AutoReceivedMessage => autoReceiveMessage(l) + case l: AutoReceivedMessage => autoReceiveMessage(l) case msg if self.hotswap.nonEmpty && self.hotswap.head.isDefinedAt(msg) => self.hotswap.head.apply(msg) case msg if self.hotswap.isEmpty && diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index d1092a7d0a..ed53eaf151 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -829,7 +829,16 @@ class LocalActorRef private[akka] ( else { currentMessage = messageHandle try { - dispatch(messageHandle) + Actor.log.slf4j.trace("Invoking actor with message: {}\n", messageHandle) + try { + cancelReceiveTimeout // FIXME: leave this here? + actor(messageHandle.message) + } catch { + case e: InterruptedException => {} // received message while actor is shutting down, ignore + case e => handleExceptionInDispatch(e, messageHandle.message) + } finally { + checkReceiveTimeout // Reschedule receive timeout + } } catch { case e => Actor.log.slf4j.error("Could not invoke actor [{}]", this) @@ -1003,22 +1012,6 @@ class LocalActorRef private[akka] ( a } - private def dispatch[T](messageHandle: MessageInvocation) = { - Actor.log.slf4j.trace("Invoking actor with message: {}\n", messageHandle) - val message = messageHandle.message //serializeMessage(messageHandle.message) - - try { - cancelReceiveTimeout // FIXME: leave this here? - actor(message) - } catch { - case e: InterruptedException => {} // received message while actor is shutting down, ignore - case e => handleExceptionInDispatch(e, message) - } - finally { - checkReceiveTimeout // Reschedule receive timeout - } - } - private def shutDownTemporaryActor(temporaryActor: ActorRef) { Actor.log.slf4j.info("Actor [{}] configured as TEMPORARY and will not be restarted.", temporaryActor.id) temporaryActor.stop From fb27cade64720c207fe56246b59085ff4d760221 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sun, 20 Feb 2011 22:32:36 +0100 Subject: [PATCH 6/8] Added a couple of final declarations on methods and reduced volatile reads --- .../src/main/scala/akka/actor/Actor.scala | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index ce2d416aca..21d5a9eb14 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -411,7 +411,7 @@ trait Actor extends Logging { /** * Is the actor able to handle the message passed in as arguments? */ - def isDefinedAt(message: Any): Boolean = processingBehavior.isDefinedAt(message) + final def isDefinedAt(message: Any): Boolean = processingBehavior.isDefinedAt(message) /** * Changes tha Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler. @@ -428,13 +428,17 @@ trait Actor extends Logging { /** * Reverts the Actor behavior to the previous one in the hotswap stack. */ - def unbecome: Unit = if (!self.hotswap.isEmpty) self.hotswap = self.hotswap.pop + def unbecome: Unit = { + val h = self.hotwap + if (h.nonEmpty) + self.hotswap = h.pop + } // ========================================= // ==== INTERNAL IMPLEMENTATION DETAILS ==== // ========================================= - private[akka] def apply(msg: Any) = fullBehavior(msg) + private[akka] final def apply(msg: Any) = fullBehavior(msg) private final def autoReceiveMessage(msg: AutoReceivedMessage) { msg match { @@ -445,12 +449,14 @@ trait Actor extends Logging { case Unlink(child) => self.unlink(child) case UnlinkAndStop(child) => self.unlink(child); child.stop case Restart(reason) => throw reason - case PoisonPill => if(self.senderFuture.isDefined) { - self.senderFuture.get.completeWithException( - new ActorKilledException("PoisonPill") - ) - } - self.stop + case PoisonPill => { + val f = self.senderFuture + if(f.isDefined) { + f.get.completeWithException(new ActorKilledException("PoisonPill")) + } + self.stop + } + } } From 994736fe5e9faf4e535464b0a760bb4487757627 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 21 Feb 2011 02:48:32 +0100 Subject: [PATCH 7/8] Added some minor migration comments for Scala 2.9.0 --- akka-actor/src/main/scala/akka/actor/Actor.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 21d5a9eb14..eec6b70474 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -429,7 +429,7 @@ trait Actor extends Logging { * Reverts the Actor behavior to the previous one in the hotswap stack. */ def unbecome: Unit = { - val h = self.hotwap + val h = self.hotswap if (h.nonEmpty) self.hotswap = h.pop } @@ -438,7 +438,7 @@ trait Actor extends Logging { // ==== INTERNAL IMPLEMENTATION DETAILS ==== // ========================================= - private[akka] final def apply(msg: Any) = fullBehavior(msg) + private[akka] final def apply(msg: Any) = fullBehavior(msg) //TODO: Scala 2.9.0 => processingBehavior.applyOrElse(msg, unhandledMsgFun) private final def autoReceiveMessage(msg: AutoReceivedMessage) { msg match { @@ -472,7 +472,8 @@ trait Actor extends Logging { } actorBehavior } - + + //TODO: Scala2.9.0 replace with: val unhandledMsgFun: Any => Unit = unhandled _ private lazy val fullBehavior: Receive = { val defaultBehavior = receive val actorBehavior: Receive = { From 169d97ee5f6a5f85e2b9ff8404a0154ab9079086 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 22 Feb 2011 19:21:45 +0100 Subject: [PATCH 8/8] Fixing a regression in Actor --- akka-actor/src/main/scala/akka/actor/Actor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index eec6b70474..8c3e478644 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -411,7 +411,7 @@ trait Actor extends Logging { /** * Is the actor able to handle the message passed in as arguments? */ - final def isDefinedAt(message: Any): Boolean = processingBehavior.isDefinedAt(message) + def isDefinedAt(message: Any): Boolean = processingBehavior.isDefinedAt(message) /** * Changes tha Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.