This is an automated email from the ASF dual-hosted git repository. He-Pin pushed a commit to branch perf/onSpinWait-cas-loops in repository https://gitbox.apache.org/repos/asf/pekko.git
commit cae9d23d770406329f3baabc4c0e0b886e044b3e Author: 虎鸣 <[email protected]> AuthorDate: Tue Jun 23 12:52:05 2026 +0800 perf: add Thread.onSpinWait() to CAS spin loops Motivation: CAS retry loops in AbstractDispatcher and Mailbox spin without any CPU hint when contention causes compareAndSet to fail. JDK 9's Thread.onSpinWait() hints to the CPU that we're in a spin-wait loop, improving power efficiency and reducing context-switch latency. Modification: - AbstractDispatcher.scala: add onSpinWait() in shutdown schedule CAS retry loop body - Mailbox.scala: convert setAsScheduled() and setAsIdle() from @tailrec recursive CAS loops to while loops with onSpinWait(), re-reading currentStatus on each retry Result: Better CPU behavior under contention on the message dispatch hot path (setAsScheduled is called on every registerForExecution). The onSpinWait pattern is already established in the codebase (AbstractNodeQueue, AbstractBoundedNodeQueue, AffinityPool). Tests: sbt "actor/compile" — passed References: Refs #3136 --- .../apache/pekko/dispatch/AbstractDispatcher.scala | 2 +- .../scala/org/apache/pekko/dispatch/Mailbox.scala | 26 +++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/actor/src/main/scala/org/apache/pekko/dispatch/AbstractDispatcher.scala b/actor/src/main/scala/org/apache/pekko/dispatch/AbstractDispatcher.scala index f5e9e277dc..03181a0e6f 100644 --- a/actor/src/main/scala/org/apache/pekko/dispatch/AbstractDispatcher.scala +++ b/actor/src/main/scala/org/apache/pekko/dispatch/AbstractDispatcher.scala @@ -255,7 +255,7 @@ abstract class MessageDispatcher(val configurator: MessageDispatcherConfigurator try { if (inhabitants == 0) shutdown() // Warning, racy } finally { - while (!updateShutdownSchedule(shutdownSchedule, UNSCHEDULED)) {} + while (!updateShutdownSchedule(shutdownSchedule, UNSCHEDULED)) { Thread.onSpinWait() } } case RESCHEDULED => if (updateShutdownSchedule(RESCHEDULED, SCHEDULED)) scheduleShutdownAction() diff --git a/actor/src/main/scala/org/apache/pekko/dispatch/Mailbox.scala b/actor/src/main/scala/org/apache/pekko/dispatch/Mailbox.scala index af728fabf9..04917f3193 100644 --- a/actor/src/main/scala/org/apache/pekko/dispatch/Mailbox.scala +++ b/actor/src/main/scala/org/apache/pekko/dispatch/Mailbox.scala @@ -189,24 +189,28 @@ private[pekko] abstract class Mailbox(val messageQueue: MessageQueue) /** * Set Scheduled status, keeping primary status as is. */ - @tailrec final def setAsScheduled(): Boolean = { - val s = currentStatus - /* - * Only try to add Scheduled bit if pure Open/Suspended, not Closed or with - * Scheduled bit already set. - */ - if ((s & shouldScheduleMask) != Open) false - else updateStatus(s, s | Scheduled) || setAsScheduled() + var s = currentStatus + while ({ + if ((s & shouldScheduleMask) != Open) return false + !updateStatus(s, s | Scheduled) + }) { + Thread.onSpinWait() + s = currentStatus + } + true } /** * Reset Scheduled status, keeping primary status as is. */ - @tailrec final def setAsIdle(): Boolean = { - val s = currentStatus - updateStatus(s, s & ~Scheduled) || setAsIdle() + var s = currentStatus + while (!updateStatus(s, s & ~Scheduled)) { + Thread.onSpinWait() + s = currentStatus + } + true } /* * AtomicReferenceFieldUpdater for system queue. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
