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

fanningpj 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 e29c133a47 Port akka-core optimizations for typed actor context and 
system adapter (#2750)
e29c133a47 is described below

commit e29c133a473eb0b25e86c9ca8546e27749c65a0c
Author: PJ Fanning <[email protected]>
AuthorDate: Wed Mar 18 10:03:10 2026 +0100

    Port akka-core optimizations for typed actor context and system adapter 
(#2750)
    
    * Initial plan
    
    * Port akka-core PRs 31843 and 31842: optimize typed actor context and 
system adapter
    
    Co-authored-by: pjfanning <[email protected]>
    
    * scalafmt
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] 
<[email protected]>
    Co-authored-by: pjfanning <[email protected]>
---
 .../pekko/actor/typed/MailboxSelectorSpec.scala    |  2 +-
 .../typed/internal/adapter/ActorAdapter.scala      |  2 +-
 .../internal/adapter/ActorContextAdapter.scala     | 46 +++++++++++-----------
 .../internal/adapter/ActorSystemAdapter.scala      | 17 +++++++-
 .../scala/org/apache/pekko/actor/ActorSystem.scala |  7 ++++
 5 files changed, 47 insertions(+), 27 deletions(-)

diff --git 
a/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/MailboxSelectorSpec.scala
 
b/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/MailboxSelectorSpec.scala
index 36a029a634..0b7bfbb04f 100644
--- 
a/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/MailboxSelectorSpec.scala
+++ 
b/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/MailboxSelectorSpec.scala
@@ -45,7 +45,7 @@ class MailboxSelectorSpec extends 
ScalaTestWithActorTestKit("""
         case WhatsYourMailbox(replyTo) =>
           val mailbox = context match {
             case adapter: ActorContextAdapter[_] =>
-              adapter.classicContext match {
+              adapter.classicActorContext match {
                 case cell: ActorCell =>
                   cell.mailbox.messageQueue
                 case unexpected => throw new RuntimeException(s"Unexpected: 
$unexpected")
diff --git 
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
 
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
index da1604cd3b..872cfc4298 100644
--- 
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
+++ 
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
@@ -72,7 +72,7 @@ import pekko.util.OptionVal
   // when the adapter is used for the user guardian (which avoids touching 
context until it is safe)
   private var _ctx: ActorContextAdapter[T] = _
   def ctx: ActorContextAdapter[T] = {
-    if (_ctx eq null) _ctx = new ActorContextAdapter[T](context, this)
+    if (_ctx eq null) _ctx = new ActorContextAdapter[T](this)
     _ctx
   }
 
diff --git 
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorContextAdapter.scala
 
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorContextAdapter.scala
index 018d5c1e72..95be6cd5de 100644
--- 
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorContextAdapter.scala
+++ 
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorContextAdapter.scala
@@ -28,7 +28,7 @@ private[pekko] object ActorContextAdapter {
 
   private def toClassicImp[U](context: TypedActorContext[_]): 
classic.ActorContext =
     context match {
-      case adapter: ActorContextAdapter[_] => adapter.classicContext
+      case adapter: ActorContextAdapter[_] => adapter.classicActorContext
       case _                               =>
         throw new UnsupportedOperationException(
           "Only adapted classic ActorContext permissible " +
@@ -45,9 +45,7 @@ private[pekko] object ActorContextAdapter {
 /**
  * INTERNAL API. Wrapping an [[pekko.actor.ActorContext]] as an 
[[TypedActorContext]].
  */
-@InternalApi private[pekko] final class ActorContextAdapter[T](
-    val classicContext: classic.ActorContext,
-    adapter: ActorAdapter[T])
+@InternalApi private[pekko] final class ActorContextAdapter[T](adapter: 
ActorAdapter[T])
     extends ActorContextImpl[T] {
 
   import ActorRefAdapter.toClassic
@@ -60,28 +58,28 @@ private[pekko] object ActorContextAdapter {
   private var _self: OptionVal[ActorRef[T]] = OptionVal.None
   override def self: ActorRef[T] = {
     if (_self.isEmpty) {
-      _self = OptionVal.Some(ActorRefAdapter(classicContext.self))
+      _self = OptionVal.Some(ActorRefAdapter(classicActorContext.self))
     }
     _self.get
   }
-  final override val system = ActorSystemAdapter(classicContext.system)
-  private[pekko] def classicActorContext = classicContext
+  override def system = ActorSystemAdapter(classicActorContext.system)
+  private[pekko] def classicActorContext = adapter.context
   override def children: Iterable[ActorRef[Nothing]] = {
     checkCurrentActorThread()
-    classicContext.children.map(ActorRefAdapter(_))
+    classicActorContext.children.map(ActorRefAdapter(_))
   }
   override def child(name: String): Option[ActorRef[Nothing]] = {
     checkCurrentActorThread()
-    classicContext.child(name).map(ActorRefAdapter(_))
+    classicActorContext.child(name).map(ActorRefAdapter(_))
   }
   override def spawnAnonymous[U](behavior: Behavior[U], props: Props = 
Props.empty): ActorRef[U] = {
     checkCurrentActorThread()
-    ActorRefFactoryAdapter.spawnAnonymous(classicContext, behavior, props, 
rethrowTypedFailure = true)
+    ActorRefFactoryAdapter.spawnAnonymous(classicActorContext, behavior, 
props, rethrowTypedFailure = true)
   }
 
   override def spawn[U](behavior: Behavior[U], name: String, props: Props = 
Props.empty): ActorRef[U] = {
     checkCurrentActorThread()
-    ActorRefFactoryAdapter.spawn(classicContext, behavior, name, props, 
rethrowTypedFailure = true)
+    ActorRefFactoryAdapter.spawn(classicActorContext, behavior, name, props, 
rethrowTypedFailure = true)
   }
 
   override def stop[U](child: ActorRef[U]): Unit = {
@@ -89,12 +87,12 @@ private[pekko] object ActorContextAdapter {
     if (child.path.parent == self.path) { // only if a direct child
       toClassic(child) match {
         case f: pekko.actor.FunctionRef =>
-          val cell = classicContext.asInstanceOf[pekko.actor.ActorCell]
+          val cell = classicActorContext.asInstanceOf[pekko.actor.ActorCell]
           cell.removeFunctionRef(f)
         case c =>
-          classicContext.child(child.path.name) match {
+          classicActorContext.child(child.path.name) match {
             case Some(`c`) =>
-              classicContext.stop(c)
+              classicActorContext.stop(c)
             case _ =>
             // child that was already stopped
           }
@@ -115,37 +113,37 @@ private[pekko] object ActorContextAdapter {
 
   override def watch[U](other: ActorRef[U]): Unit = {
     checkCurrentActorThread()
-    classicContext.watch(toClassic(other))
+    classicActorContext.watch(toClassic(other))
   }
   override def watchWith[U](other: ActorRef[U], msg: T): Unit = {
     checkCurrentActorThread()
-    classicContext.watchWith(toClassic(other), msg)
+    classicActorContext.watchWith(toClassic(other), msg)
   }
   override def unwatch[U](other: ActorRef[U]): Unit = {
     checkCurrentActorThread()
-    classicContext.unwatch(toClassic(other))
+    classicActorContext.unwatch(toClassic(other))
   }
   var receiveTimeoutMsg: T = null.asInstanceOf[T]
   override def setReceiveTimeout(d: FiniteDuration, msg: T): Unit = {
     checkCurrentActorThread()
     receiveTimeoutMsg = msg
-    classicContext.setReceiveTimeout(d)
+    classicActorContext.setReceiveTimeout(d)
   }
   override def cancelReceiveTimeout(): Unit = {
     checkCurrentActorThread()
 
     receiveTimeoutMsg = null.asInstanceOf[T]
-    classicContext.setReceiveTimeout(Duration.Undefined)
+    classicActorContext.setReceiveTimeout(Duration.Undefined)
   }
-  override def executionContext: ExecutionContextExecutor = 
classicContext.dispatcher
+  override def executionContext: ExecutionContextExecutor = 
classicActorContext.dispatcher
   override def scheduleOnce[U](delay: FiniteDuration, target: ActorRef[U], 
msg: U): classic.Cancellable = {
-    import classicContext.dispatcher
-    classicContext.system.scheduler.scheduleOnce(delay, toClassic(target), msg)
+    classicActorContext.system.scheduler.scheduleOnce(delay, 
toClassic(target), msg)(classicActorContext.dispatcher)
   }
   override private[pekko] def internalSpawnMessageAdapter[U](f: U => T, _name: 
String): ActorRef[U] = {
-    val cell = classicContext.asInstanceOf[pekko.actor.ActorCell]
+    val cell = classicActorContext.asInstanceOf[pekko.actor.ActorCell]
     // apply the function inside the actor by wrapping the msg and f, handled 
by ActorAdapter
-    val ref = cell.addFunctionRef((_, msg) => classicContext.self ! 
AdaptMessage[U, T](msg.asInstanceOf[U], f), _name)
+    val ref =
+      cell.addFunctionRef((_, msg) => classicActorContext.self ! 
AdaptMessage[U, T](msg.asInstanceOf[U], f), _name)
     ActorRefAdapter[U](ref)
   }
 
diff --git 
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorSystemAdapter.scala
 
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorSystemAdapter.scala
index 2cf322c878..7c9bfb6ca8 100644
--- 
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorSystemAdapter.scala
+++ 
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorSystemAdapter.scala
@@ -41,6 +41,7 @@ import 
pekko.actor.typed.internal.PropsImpl.DispatcherSameAsParent
 import pekko.actor.typed.internal.SystemMessage
 import pekko.actor.typed.scaladsl.Behaviors
 import pekko.annotation.InternalApi
+import pekko.util.OptionVal
 
 import org.slf4j.{ Logger, LoggerFactory }
 
@@ -141,7 +142,21 @@ import org.slf4j.{ Logger, LoggerFactory }
 }
 
 private[pekko] object ActorSystemAdapter {
-  def apply(system: classic.ActorSystem): ActorSystem[Nothing] = 
AdapterExtension(system).adapter
+  def apply(system: classic.ActorSystem): ActorSystem[Nothing] = {
+    system match {
+      case system: classic.ActorSystemImpl =>
+        // Optimization to cut out going through adapter lookup if possible
+        system.typedSystem match {
+          case OptionVal.Some(typedSystem: ActorSystem[_]) => typedSystem
+          case _                                           =>
+            val typedSystem: ActorSystem[_] = AdapterExtension(system).adapter
+            system.typedSystem = OptionVal.Some(typedSystem)
+            typedSystem
+        }
+      case _ =>
+        AdapterExtension(system).adapter
+    }
+  }
 
   // to make sure we do never create more than one adapter for the same actor 
system
   class AdapterExtension(system: classic.ExtendedActorSystem) extends 
classic.Extension {
diff --git a/actor/src/main/scala/org/apache/pekko/actor/ActorSystem.scala 
b/actor/src/main/scala/org/apache/pekko/actor/ActorSystem.scala
index 3a6cfe2900..95a263241b 100644
--- a/actor/src/main/scala/org/apache/pekko/actor/ActorSystem.scala
+++ b/actor/src/main/scala/org/apache/pekko/actor/ActorSystem.scala
@@ -848,6 +848,13 @@ private[pekko] class ActorSystemImpl(
 
   private val _dynamicAccess: DynamicAccess = createDynamicAccess()
 
+  /**
+   * Optimistic optimization: Tries to avoid going through the extension 
infrastructure if possible when using
+   * the typed system. Will contain a org.apache.pekko.actor.typed.ActorSystem 
if set, should not be touched by
+   * anything but the typed ActorSystemAdapter.
+   */
+  private[pekko] var typedSystem: OptionVal[AnyRef] = OptionVal.None
+
   final val settings: Settings = {
     val config = Settings.amendSlf4jConfig(
       
applicationConfig.withFallback(ConfigFactory.defaultReference(classLoader)),


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

Reply via email to