better document become semantics, see #2683

This commit is contained in:
Roland 2012-11-06 11:00:27 +01:00
parent 8eec825f18
commit f9eb59e883
7 changed files with 54 additions and 57 deletions

View file

@ -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

View file

@ -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()
* }

View file

@ -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);
}

View file

@ -549,7 +549,8 @@ Upgrade
Akka supports hotswapping the Actors 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
=====

View file

@ -653,11 +653,10 @@ Upgrade
-------
Akka supports hotswapping the Actors 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 <https://github.com/akka/akka/blob/master/akka-docs/scala/code/docs/actor/UnnestedReceives.scala>`_.
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

View file

@ -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
}
}

View file

@ -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 <http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html>`_ which provide a pretty easy-worked API to intercept method calls.