diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index 8108626ab4..14d280eb98 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -76,8 +76,14 @@ trait ActorContext extends ActorRefFactory { /** * Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler. - * Puts the behavior on top of the hotswap stack. - * If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack + * This method acts upon the behavior stack as follows: + * + * - if `discardOld = true` it will replace the top element (i.e. the current behavior) + * - if `discardOld = false` it will keep the current behavior and push the given one atop + * + * The default of replacing the current behavior has been chosen to avoid memory leaks in + * case client code is written without consulting this documentation first (i.e. always pushing + * new closures and never issuing an `unbecome()`) */ def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit @@ -167,14 +173,20 @@ trait UntypedActorContext extends ActorContext { /** * Changes the Actor's behavior to become the new 'Procedure' handler. - * Puts the behavior on top of the hotswap stack. + * Replaces the current behavior at the top of the hotswap stack. */ def become(behavior: Procedure[Any]): Unit /** * Changes the Actor's behavior to become the new 'Procedure' handler. - * Puts the behavior on top of the hotswap stack. - * If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack + * This method acts upon the behavior stack as follows: + * + * - if `discardOld = true` it will replace the top element (i.e. the current behavior) + * - if `discardOld = false` it will keep the current behavior and push the given one atop + * + * The default of replacing the current behavior has been chosen to avoid memory leaks in + * case client code is written without consulting this documentation first (i.e. always pushing + * new closures and never issuing an `unbecome()`) */ def become(behavior: Procedure[Any], discardOld: Boolean): Unit diff --git a/akka-actor/src/main/scala/akka/actor/Stash.scala b/akka-actor/src/main/scala/akka/actor/Stash.scala index 05b618d03a..cdf4ef6d5b 100644 --- a/akka-actor/src/main/scala/akka/actor/Stash.scala +++ b/akka-actor/src/main/scala/akka/actor/Stash.scala @@ -16,13 +16,13 @@ import akka.AkkaException * def receive = { * case "open" ⇒ * unstashAll() - * context.become { + * context.become({ * case "write" ⇒ // do writing... * case "close" ⇒ * unstashAll() * context.unbecome() * case msg ⇒ stash() - * } + * }, discardOld = false) * case "done" ⇒ // done * case msg ⇒ stash() * } diff --git a/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java b/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java index c882ac015a..5098278a38 100644 --- a/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java +++ b/akka-docs/rst/java/code/docs/actor/UntypedActorSwapper.java @@ -32,9 +32,9 @@ public class UntypedActorSwapper { @Override public void apply(Object message) { log.info("Ho"); - getContext().unbecome(); // resets the latest 'become' (just for fun) + getContext().unbecome(); // resets the latest 'become' } - }); + }, false); // this signals stacking of the new behavior } else { unhandled(message); } diff --git a/akka-docs/rst/java/untyped-actors.rst b/akka-docs/rst/java/untyped-actors.rst index 2ee8bc397f..c708188d07 100644 --- a/akka-docs/rst/java/untyped-actors.rst +++ b/akka-docs/rst/java/untyped-actors.rst @@ -549,7 +549,8 @@ Upgrade Akka supports hotswapping the Actor’s message loop (e.g. its implementation) at runtime. Use the ``getContext().become`` method from within the Actor. -The hotswapped code is kept in a Stack which can be pushed and popped. +The hotswapped code is kept in a Stack which can be pushed (replacing or adding +at the top) and popped. .. warning:: @@ -563,26 +564,20 @@ To hotswap the Actor using ``getContext().become``: .. includecode:: code/docs/actor/UntypedActorDocTestBase.java :include: hot-swap-actor -The ``become`` method is useful for many different things, such as to implement -a Finite State Machine (FSM). +This variant of the :meth:`become` method is useful for many different things, +such as to implement a Finite State Machine (FSM). It will replace the current +behavior (i.e. the top of the behavior stack), which means that +:meth:`unbecome` is not called, instead always the next behavior is explicitly +installed. -Here is another little cute example of ``become`` and ``unbecome`` in action: +The other way of using :meth:`become` does not replace but add to the top of +the behavior stack. In this case care must be taken to ensure that the number +of “pop” operations (i.e. :meth:`unbecome`) matches the number of “push” ones +in the long run, otherwise this amounts to a memory leak (which is why this +behavior is not the default). .. includecode:: code/docs/actor/UntypedActorSwapper.java#swapper -Downgrade ---------- - -Since the hotswapped code is pushed to a Stack you can downgrade the code as -well. Use the ``getContext().unbecome`` method from within the Actor. - -.. code-block:: java - - public void onReceive(Object message) { - if (message.equals("revert")) getContext().unbecome(); - } - - Stash ===== diff --git a/akka-docs/rst/scala/actors.rst b/akka-docs/rst/scala/actors.rst index e81f744952..6e90d589ae 100644 --- a/akka-docs/rst/scala/actors.rst +++ b/akka-docs/rst/scala/actors.rst @@ -653,11 +653,10 @@ Upgrade ------- Akka supports hotswapping the Actor’s message loop (e.g. its implementation) at -runtime: Invoke the ``context.become`` method from within the Actor. - -Become takes a ``PartialFunction[Any, Unit]`` that implements -the new message handler. The hotswapped code is kept in a Stack which can be -pushed and popped. +runtime: invoke the ``context.become`` method from within the Actor. +:meth:`become` takes a ``PartialFunction[Any, Unit]`` that implements the new +message handler. The hotswapped code is kept in a Stack which can be pushed and +popped. .. warning:: @@ -667,38 +666,26 @@ To hotswap the Actor behavior using ``become``: .. includecode:: code/docs/actor/ActorDocSpec.scala#hot-swap-actor -The ``become`` method is useful for many different things, but a particular nice -example of it is in example where it is used to implement a Finite State Machine -(FSM): `Dining Hakkers`_. +This variant of the :meth:`become` method is useful for many different things, +such as to implement a Finite State Machine (FSM, for an example see `Dining +Hakkers`_). It will replace the current behavior (i.e. the top of the behavior +stack), which means that :meth:`unbecome` is not called, instead always the +next behavior is explicitly installed. .. _Dining Hakkers: @github@/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala -Here is another little cute example of ``become`` and ``unbecome`` in action: +The other way of using :meth:`become` does not replace but add to the top of +the behavior stack. In this case care must be taken to ensure that the number +of “pop” operations (i.e. :meth:`unbecome`) matches the number of “push” ones +in the long run, otherwise this amounts to a memory leak (which is why this +behavior is not the default). .. includecode:: code/docs/actor/ActorDocSpec.scala#swapper Encoding Scala Actors nested receives without accidentally leaking memory ------------------------------------------------------------------------- -See this `Unnested receive example `_. - - -Downgrade ---------- - -Since the hotswapped code is pushed to a Stack you can downgrade the code as -well, all you need to do is to: Invoke the ``context.unbecome`` method from within the Actor. - -This will pop the Stack and replace the Actor's implementation with the -``PartialFunction[Any, Unit]`` that is at the top of the Stack. - -Here's how you use the ``unbecome`` method: - -.. code-block:: scala - - def receive = { - case "revert" => context.unbecome() - } +See this `Unnested receive example <@github@/akka-docs/scala/code/docs/actor/UnnestedReceives.scala>`_. Stash diff --git a/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala b/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala index 611b7a43f5..6642df3b28 100644 --- a/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala @@ -96,11 +96,11 @@ class Swapper extends Actor { def receive = { case Swap ⇒ log.info("Hi") - become { + become({ case Swap ⇒ log.info("Ho") unbecome() // resets the latest 'become' (just for fun) - } + }, discardOld = false) // push on top instead of replace } } diff --git a/akka-docs/rst/scala/typed-actors.rst b/akka-docs/rst/scala/typed-actors.rst index ce9c608e4e..0a0597cf0d 100644 --- a/akka-docs/rst/scala/typed-actors.rst +++ b/akka-docs/rst/scala/typed-actors.rst @@ -7,7 +7,10 @@ Essentially turning method invocations into asynchronous dispatch instead of syn Typed Actors consist of 2 "parts", a public interface and an implementation, and if you've done any work in "enterprise" Java, this will be very familiar to you. As with normal Actors you have an external API (the public interface instance) that will delegate methodcalls asynchronously to a private instance of the implementation. -The advantage of Typed Actors vs. Actors is that with TypedActors you have a static contract, and don't need to define your own messages, the downside is that it places some limitations on what you can do and what you can't, i.e. you can't use become/unbecome. +The advantage of Typed Actors vs. Actors is that with TypedActors you have a +static contract, and don't need to define your own messages, the downside is +that it places some limitations on what you can do and what you can't, i.e. you +cannot use :meth:`become`/:meth:`unbecome`. Typed Actors are implemented using `JDK Proxies `_ which provide a pretty easy-worked API to intercept method calls.