optimize Mailbox._status usage

- make Scheduled==4 to allow bitwise operations
- unfold become(status) into three methods for better inlining
- optimize patterns in setAsIdle away: table switch is probably slower
  than a single conditional
- fix LoggingReceiveSpec glitch wrt. async stop()
This commit is contained in:
Roland 2011-10-04 14:28:05 +02:00
parent 4df9d621bb
commit 4b5c99e9bd
4 changed files with 78 additions and 63 deletions

View file

@ -134,6 +134,7 @@ class LoggingReceiveSpec
})
actor ! PoisonPill
expectMsg(300 millis, EventHandler.Debug(actor.underlyingActor, "received AutoReceiveMessage PoisonPill"))
awaitCond(actor.isShutdown, 100 millis)
}
"log LifeCycle changes if requested" in {

View file

@ -7,6 +7,6 @@ package akka.dispatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
abstract class AbstractMailbox {
private volatile int _status = Mailbox.Idle();
private volatile int _status; // not initialized because this is faster: 0 == Open
protected final static AtomicIntegerFieldUpdater<AbstractMailbox> updater = AtomicIntegerFieldUpdater.newUpdater(AbstractMailbox.class, "_status");
}

View file

@ -88,7 +88,7 @@ abstract class MessageDispatcher extends Serializable {
protected[akka] val deadLetterMailbox: Mailbox = DeadLetterMailbox
object DeadLetterMailbox extends Mailbox {
become(Mailbox.Closed)
becomeClosed()
override def dispatcher = null //MessageDispatcher.this
override def enqueue(envelope: Envelope) { envelope.channel sendException new ActorKilledException("Actor has been stopped") }
override def dequeue() = null
@ -185,7 +185,7 @@ abstract class MessageDispatcher extends Serializable {
protected[akka] def unregister(actor: ActorCell) {
if (uuids remove actor.uuid) {
val mailBox = actor.mailbox
mailBox.become(Mailbox.Closed)
mailBox.becomeClosed()
actor.mailbox = deadLetterMailbox //FIXME getAndSet would be preferrable here
cleanUpMailboxFor(actor, mailBox)
} else System.err.println("Couldn't unregister: " + actor)
@ -244,14 +244,14 @@ abstract class MessageDispatcher extends Serializable {
* After the call to this method, the dispatcher mustn't begin any new message processing for the specified reference
*/
def suspend(actor: ActorCell): Unit =
if (uuids.contains(actor.uuid)) actor.mailbox.become(Mailbox.Suspended)
if (uuids.contains(actor.uuid)) actor.mailbox.becomeSuspended()
/*
* After the call to this method, the dispatcher must begin any new message processing for the specified reference
*/
def resume(actor: ActorCell): Unit = if (uuids.contains(actor.uuid)) {
val mbox = actor.mailbox
mbox.become(Mailbox.Idle)
mbox.becomeOpen()
registerForExecution(mbox, false, false)
}

View file

@ -19,13 +19,16 @@ private[dispatch] object Mailbox {
type Status = Int
/*
* the following assigned numbers CANNOT be changed without looking at the code which uses them!
*/
// primary status: only first three
final val Idle = 0
final val Open = 0 // _status is not initialized in AbstractMailbox, so default must be zero!
final val Suspended = 1
final val Closed = 2
// secondary status: Idle or Suspended plus Scheduled
final val Scheduled = 3
final val ScheduledSuspended = 4 // may only happen for system message processing
// secondary status: Scheduled bit may be added to Open/Suspended
final val Scheduled = 4
}
/**
@ -36,23 +39,13 @@ abstract class Mailbox extends AbstractMailbox with MessageQueue with SystemMess
final def status: Mailbox.Status = AbstractMailbox.updater.get(this)
final def isActive: Boolean = status match {
case Idle | Scheduled true
case _ false
}
final def isActive: Boolean = (status & 3) == Open
// FIXME: this does not seem to be used anywhere, remove?
final def isSuspended: Boolean = status match {
case Suspended | ScheduledSuspended true
case _ false
}
final def isSuspended: Boolean = (status & 3) == Suspended
final def isClosed: Boolean = status == Closed
final def isScheduled: Boolean = status match {
case Scheduled | ScheduledSuspended true
case _ false
}
final def isScheduled: Boolean = (status & Scheduled) != 0
@inline
protected final def updateStatus(oldStatus: Status, newStatus: Status): Boolean =
@ -73,52 +66,73 @@ abstract class Mailbox extends AbstractMailbox with MessageQueue with SystemMess
}
/**
* set new primary status: Idle, Suspended or Closed. Caller does not need to
* worry about whether status was Scheduled or not.
* set new primary status Open. Caller does not need to worry about whether
* status was Scheduled or not.
*/
@tailrec
final def become(newStatus: Status): Boolean = {
newStatus match {
case Idle
status match {
case s @ (Idle | Scheduled) if (updateStatus(s, s)) true else become(newStatus)
case s @ Suspended if (updateStatus(s, Idle)) true else become(newStatus)
case s @ ScheduledSuspended if (updateStatus(s, Scheduled)) true else become(newStatus)
final def becomeOpen(): Boolean = status match {
case Closed setStatus(Closed); false
}
case Suspended
status match {
case s @ (Suspended | ScheduledSuspended) if (updateStatus(s, s)) true else become(newStatus)
case s @ Idle if (updateStatus(s, Suspended)) true else become(newStatus)
case s @ Scheduled if (updateStatus(s, ScheduledSuspended)) true else become(newStatus)
case Closed setStatus(Closed); false
}
case Closed
status match {
case Closed setStatus(Closed); false
case s if (updateStatus(s, Closed)) true else become(newStatus)
}
}
case s updateStatus(s, Open | s & Scheduled) || becomeOpen()
}
/**
* set new primary status Suspended. Caller does not need to worry about whether
* status was Scheduled or not.
*/
@tailrec
final def setAsScheduled(): Boolean = status match {
case s @ Idle if (updateStatus(s, Scheduled)) true else setAsScheduled()
case s @ Suspended if (updateStatus(s, ScheduledSuspended)) true else setAsScheduled()
case _ false
final def becomeSuspended(): Boolean = status match {
case Closed setStatus(Closed); false
case s updateStatus(s, Suspended | s & Scheduled) || becomeSuspended()
}
/**
* set new primary status Closed. Caller does not need to worry about whether
* status was Scheduled or not.
*/
@tailrec
final def setAsIdle(): Boolean = status match {
case s @ Scheduled if (updateStatus(s, Idle)) true else setAsIdle()
case s @ ScheduledSuspended if (updateStatus(s, Suspended)) true else setAsIdle()
case s acknowledgeStatus(); false
final def becomeClosed(): Boolean = status match {
case Closed setStatus(Closed); false
case s updateStatus(s, Closed) || becomeClosed()
}
/**
* Set Scheduled status, keeping primary status as is.
*/
@tailrec
final def setAsScheduled(): Boolean = {
val s = status
/*
* only try to add Scheduled bit if pure Open/Suspended, not Closed or with
* Scheduled bit already set (this is one of the reasons why the numbers
* cannot be changed in object Mailbox above)
*/
if (s <= Suspended) updateStatus(s, s | Scheduled) || setAsScheduled()
else false
}
/**
* Reset Scheduled status, keeping primary status as is.
*/
@tailrec
final def setAsIdle(): Boolean = {
val s = status
/*
* only try to remove Scheduled bit if currently Scheduled, not Closed or
* without Scheduled bit set (this is one of the reasons why the numbers
* cannot be changed in object Mailbox above)
*/
if (s >= Scheduled) {
updateStatus(s, s & ~Scheduled) || setAsIdle()
} else {
acknowledgeStatus() // this write is needed to make memory consistent after processMailbox()
false
}
}
def shouldBeRegisteredForExecution(hasMessageHint: Boolean, hasSystemMessageHint: Boolean): Boolean = status match {
case Idle | Scheduled hasMessageHint || hasSystemMessageHint || hasSystemMessages || hasMessages
case Open | Scheduled hasMessageHint || hasSystemMessageHint || hasSystemMessages || hasMessages
case Closed false
case Suspended | ScheduledSuspended hasSystemMessageHint || hasSystemMessages
case _ hasSystemMessageHint || hasSystemMessages
}
final def run = {