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]

Reply via email to