This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new d75bf95017 fix: keep lazy dispatch state on VarHandle (#3116)
d75bf95017 is described below

commit d75bf95017f4188ad861a04b721c00ee600d7388
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Sun Jun 21 11:45:14 2026 +0800

    fix: keep lazy dispatch state on VarHandle (#3116)
    
    Motivation:
    PR #3035 used a VarHandle-backed volatile Int for LazyDispatch election 
state.
    Review raised concern about the field and the VarHandle access semantics.
    
    Modification:
    Keep the VarHandle-backed Int state, but use explicit 
getVolatile/setVolatile
    accessors with compareAndSet. This keeps the state in the LazyDispatch 
object
    without per-instance AtomicInteger allocation while preserving conservative
    volatile access semantics.
    
    Result:
    LazyDispatch keeps the same IDLE/SCHEDULED scheduling protocol with explicit
    volatile state access.
    
    Tests:
    - scalafmt --mode diff-ref=origin/main --non-interactive
    - scalafmt --list --mode diff-ref=origin/main --non-interactive
    - sbt "+stream / compile"
    - sbt "stream-tests / Test / testOnly 
org.apache.pekko.stream.scaladsl.StageActorRefSpec" (11/11)
    - git diff --check
    
    References:
    Refs #3035
---
 .../org/apache/pekko/stream/stage/GraphStage.scala     | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/stage/GraphStage.scala 
b/stream/src/main/scala/org/apache/pekko/stream/stage/GraphStage.scala
index 5ea8c444a9..6c04d0cfdd 100644
--- a/stream/src/main/scala/org/apache/pekko/stream/stage/GraphStage.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/stage/GraphStage.scala
@@ -13,8 +13,8 @@
 
 package org.apache.pekko.stream.stage
 
-import java.util.Spliterator
 import java.lang.invoke.{ MethodHandles, VarHandle }
+import java.util.Spliterator
 import java.util.concurrent.{ CompletionStage, ConcurrentHashMap }
 import java.util.concurrent.atomic.AtomicReference
 
@@ -359,9 +359,8 @@ object GraphStageLogic {
      *    object (one allocation per StageActor, one fewer field deref on the 
producer hot path).
      *  - Implements `Any => Unit` directly — serves as both the producer 
callback and the drain callback,
      *    eliminating the separate `drainCallback` lambda allocation.
-     *  - `state` is a plain `@volatile var Int` driven by a static 
`VarHandle` in the companion object
-     *    (via `MethodHandles.privateLookupIn`), same pattern as 
`AbstractNodeQueue` itself;
-     *    avoids per-instance `AtomicInteger`.
+     *  - `state` is a plain `@volatile var Int` driven by a static 
`VarHandle`; volatile state access
+     *    keeps the election protocol conservative and avoids per-instance 
`AtomicInteger`.
      *  - `drainBatchSize` is read once into a stack-local at the top of 
`drain` so the JIT can treat the loop
      *    bound as a constant.
      *  - Per-tell allocation = 1 Node (`AbstractNodeQueue.Node`, ~24 bytes) + 
1 Tuple2 (~24 bytes). The
@@ -378,18 +377,14 @@ object GraphStageLogic {
         extends AbstractNodeQueue[(ActorRef, Any)]
         with (Any => Unit) {
 
-      // IDLE/SCHEDULED election state. VarHandle avoids per-instance 
AtomicInteger;
-      // the handle lives in the companion object (true JVM static), same 
pattern as
-      // AbstractNodeQueue._tailDoNotCallMeDirectly.
+      // IDLE/SCHEDULED election state. VarHandle avoids per-instance 
AtomicInteger.
       @volatile var state: Int = SchedStateIdle
 
-      // Typed local witnesses the Int-returning signature-polymorphic 
VarHandle.get overload
-      // (required for Scala 3 cross-compile; on Scala 2 this is a no-op).
       private def getState(): Int = {
-        val v: Int = LazyDispatch.stateHandle.get(this)
+        val v: Int = LazyDispatch.stateHandle.getVolatile(this)
         v
       }
-      private def setState(v: Int): Unit = LazyDispatch.stateHandle.set(this, 
v)
+      private def setState(v: Int): Unit = 
LazyDispatch.stateHandle.setVolatile(this, v)
       private def casState(expect: Int, update: Int): Boolean =
         LazyDispatch.stateHandle.compareAndSet(this, expect, update)
 
@@ -448,6 +443,7 @@ object GraphStageLogic {
         lookup.findVarHandle(classOf[LazyDispatch], "state", Integer.TYPE)
       }
     }
+
   }
 
   /**


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to