diff --git a/akka-actor-typed/src/main/mima-filters/2.6.19.backwards.excludes/issue-22849.excludes b/akka-actor-typed/src/main/mima-filters/2.6.19.backwards.excludes/issue-22849.excludes new file mode 100644 index 0000000000..75a6875410 --- /dev/null +++ b/akka-actor-typed/src/main/mima-filters/2.6.19.backwards.excludes/issue-22849.excludes @@ -0,0 +1,3 @@ +# #30445 performance improvements for typed javadsl message matchers +ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.actor.typed.javadsl.BuiltReceive.this") + ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.actor.typed.javadsl.BuiltBehavior.this") \ No newline at end of file diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/BehaviorBuilder.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/BehaviorBuilder.scala index 33e676f727..477c22869d 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/BehaviorBuilder.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/BehaviorBuilder.scala @@ -34,12 +34,14 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa /** * Build a Behavior from the current state of the builder */ - def build(): Behavior[T] = new BuiltBehavior(messageHandlers.reverse, signalHandlers.reverse) + def build(): Behavior[T] = { + new BuiltBehavior[T](messageHandlers.reverse.toArray, signalHandlers.reverse.toArray) + } /** * Add a new case to the message handling. * - * @param type type of message to match + * @param type type of message to match * @param handler action to apply if the type matches * @tparam M type of message to match * @return a new behavior builder with the specified handling appended @@ -50,8 +52,8 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa /** * Add a new predicated case to the message handling. * - * @param type type of message to match - * @param test a predicate that will be evaluated on the argument if the type matches + * @param type type of message to match + * @param test a predicate that will be evaluated on the argument if the type matches * @param handler action to apply if the type matches and the predicate returns true * @tparam M type of message to match * @return a new behavior builder with the specified handling appended @@ -65,7 +67,7 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa * Should normally not be used, but when matching on class with generic type * argument it can be useful, e.g. List.class and (List<String> list) -> {...} * - * @param type type of message to match + * @param type type of message to match * @param handler action to apply when the type matches * @return a new behavior builder with the specified handling appended */ @@ -75,7 +77,7 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa /** * Add a new case to the message handling matching equal messages. * - * @param msg the message to compare to + * @param msg the message to compare to * @param handler action to apply when the message matches * @return a new behavior builder with the specified handling appended */ @@ -98,7 +100,7 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa /** * Add a new case to the signal handling. * - * @param type type of signal to match + * @param type type of signal to match * @param handler action to apply if the type matches * @tparam M type of signal to match * @return a new behavior builder with the specified handling appended @@ -109,8 +111,8 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa /** * Add a new predicated case to the signal handling. * - * @param type type of signals to match - * @param test a predicate that will be evaluated on the argument if the type matches + * @param type type of signals to match + * @param test a predicate that will be evaluated on the argument if the type matches * @param handler action to apply if the type matches and the predicate returns true * @tparam M type of signal to match * @return a new behavior builder with the specified handling appended @@ -127,7 +129,7 @@ final class BehaviorBuilder[T] private (messageHandlers: List[Case[T, T]], signa /** * Add a new case to the signal handling matching equal signals. * - * @param signal the signal to compare to + * @param signal the signal to compare to * @param handler action to apply when the message matches * @return a new behavior builder with the specified handling appended */ @@ -157,6 +159,7 @@ object BehaviorBuilder { private val _empty = new BehaviorBuilder[Nothing](Nil, Nil) // used for both matching signals and messages so we throw away types after they are enforced by the builder API above + /** INTERNAL API */ @InternalApi private[javadsl] final case class Case[BT, MT]( @@ -177,23 +180,25 @@ object BehaviorBuilder { * INTERNAL API */ @InternalApi -private final class BuiltBehavior[T](messageHandlers: List[Case[T, T]], signalHandlers: List[Case[T, Signal]]) +private final class BuiltBehavior[T](messageHandlers: Array[Case[T, T]], signalHandlers: Array[Case[T, Signal]]) extends ExtensibleBehavior[T] { - override def receive(ctx: TypedActorContext[T], msg: T): Behavior[T] = receive(msg, messageHandlers) + override def receive(ctx: TypedActorContext[T], msg: T): Behavior[T] = receive(msg, messageHandlers, 0) - override def receiveSignal(ctx: TypedActorContext[T], msg: Signal): Behavior[T] = - receive(msg, signalHandlers) + override def receiveSignal(ctx: TypedActorContext[T], msg: Signal): Behavior[T] = receive(msg, signalHandlers, 0) @tailrec - private def receive[M](msg: M, handlers: List[Case[T, M]]): Behavior[T] = - handlers match { - case Case(cls, predicate, handler) :: tail => - if ((cls.isEmpty || cls.get.isAssignableFrom(msg.getClass)) && (predicate.isEmpty || predicate.get.apply(msg))) - handler(msg) - else receive(msg, tail) - case Nil => + private def receive[M](msg: M, handlers: Array[Case[T, M]], idx: Int): Behavior[T] = { + if (handlers.length == 0) { + Behaviors.unhandled[T] + } else { + val Case(cls, predicate, handler) = handlers(idx) + if ((cls.isEmpty || cls.get.isAssignableFrom(msg.getClass)) && (predicate.isEmpty || predicate.get.apply(msg))) + handler(msg) + else if (idx == handlers.length - 1) Behaviors.unhandled[T] + else + receive(msg, handlers, idx + 1) } - + } } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ReceiveBuilder.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ReceiveBuilder.scala index dea517a390..103b9633b3 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ReceiveBuilder.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/ReceiveBuilder.scala @@ -4,16 +4,13 @@ package akka.actor.typed.javadsl -import scala.annotation.tailrec - -import akka.actor.typed.{ Behavior, Signal } -import akka.actor.typed.MessageAdaptionFailure +import akka.actor.typed.{ Behavior, MessageAdaptionFailure, Signal } import akka.annotation.InternalApi -import akka.japi.function.{ Function => JFunction } -import akka.japi.function.{ Predicate => JPredicate } -import akka.japi.function.Creator +import akka.japi.function.{ Creator, Function => JFunction, Predicate => JPredicate } import akka.util.OptionVal +import scala.annotation.tailrec + /** * Mutable builder used when implementing [[AbstractBehavior]]. * @@ -33,8 +30,7 @@ final class ReceiveBuilder[T] private ( val builtSignalHandlers = if (signalHandlers.isEmpty) defaultSignalHandlers[T] else (adapterExceptionSignalHandler[T] :: signalHandlers).reverse - - new BuiltReceive[T](messageHandlers.reverse, builtSignalHandlers) + new BuiltReceive[T](messageHandlers.reverse.toArray, builtSignalHandlers.toArray) } /** @@ -196,24 +192,27 @@ object ReceiveBuilder { */ @InternalApi private final class BuiltReceive[T]( - messageHandlers: List[ReceiveBuilder.Case[T, T]], - signalHandlers: List[ReceiveBuilder.Case[T, Signal]]) + messageHandlers: Array[ReceiveBuilder.Case[T, T]], + signalHandlers: Array[ReceiveBuilder.Case[T, Signal]]) extends Receive[T] { import ReceiveBuilder.Case - override def receiveMessage(msg: T): Behavior[T] = receive[T](msg, messageHandlers) + override def receiveMessage(msg: T): Behavior[T] = receive[T](msg, messageHandlers, 0) - override def receiveSignal(msg: Signal): Behavior[T] = receive[Signal](msg, signalHandlers) + override def receiveSignal(msg: Signal): Behavior[T] = receive[Signal](msg, signalHandlers, 0) @tailrec - private def receive[M](msg: M, handlers: List[Case[T, M]]): Behavior[T] = - handlers match { - case Case(cls, predicate, handler) :: tail => - if ((cls.isEmpty || cls.get.isAssignableFrom(msg.getClass)) && (predicate.isEmpty || predicate.get.test(msg))) - handler(msg) - else receive[M](msg, tail) - case _ => - Behaviors.unhandled + private def receive[M](msg: M, handlers: Array[Case[T, M]], idx: Int): Behavior[T] = { + if (handlers.length == 0) { + Behaviors.unhandled[T] + } else { + val Case(cls, predicate, handler) = handlers(idx) + if ((cls.isEmpty || cls.get.isAssignableFrom(msg.getClass)) && (predicate.isEmpty || predicate.get.test(msg))) + handler(msg) + else if (idx == handlers.length - 1) + Behaviors.unhandled[T] + else + receive(msg, handlers, idx + 1) } - + } }