Implementing an actor using a class requires the following:
Implementing an actor using a class requires the following:
\begin{itemize}
\item Provide a constructor taking a reference of type
* Provide a constructor taking a reference of type ``actor_config&`` as first argument, which is forwarded to the base class. The config is passed implicitly to the constructor when calling ``spawn``, which also forwards any number of additional arguments to the constructor.
\lstinline^actor_config&^ as first argument, which is forwarded to the base
* Override ``make_behavior`` for event-based actors and ``act`` for blocking actors.
class. The config is passed implicitly to the constructor when calling
\lstinline^spawn^, which also forwards any number of additional arguments
to the constructor.
\item Override \lstinline^make_behavior^ for event-based actors and
\lstinline^act^ for blocking actors.
\end{itemize}
Implementing actors with classes works for all kinds of actors and allows
Implementing actors with classes works for all kinds of actors and allows
simple management of state via member variables. However, composing states via
simple management of state via member variables. However, composing states via
inheritance can get quite tedious. For dynamically typed actors, composing
inheritance can get quite tedious. For dynamically typed actors, composing
states is particularly hard, because the compiler cannot provide much help. For
states is particularly hard, because the compiler cannot provide much help. For
statically typed actors, CAF also provides an API for composable
statically typed actors, CAF also provides an API for composable
behaviors~\see{composable-behavior} that works well with inheritance. The
behaviors composable-behavior_ that works well with inheritance. The
following three examples implement the forward declarations shown in
following three examples implement the forward declarations shown in
\texttt{c} & The category/component. This name is defined by the macro \lstinline^CAF_LOG_COMPONENT^. Set this macro before including any CAF header. \\
| ``C`` | The full qualifier of the current function. For example, the qualifier of ``void ns::foo::bar()`` is printed as ``ns.foo``. |
\texttt{C} & The full qualifier of the current function. For example, the qualifier of \lstinline^void ns::foo::bar()^ is printed as \lstinline^ns.foo^. \\
Message passing in CAF is always asynchronous. Further, CAF neither guarantees
Message passing in CAF is always asynchronous. Further, CAF neither guarantees
message delivery nor message ordering in a distributed setting. CAF uses TCP
message delivery nor message ordering in a distributed setting. CAF uses TCP
...
@@ -9,29 +11,34 @@ intermediate nodes and can get lost if one of the forwarding nodes fails.
...
@@ -9,29 +11,34 @@ intermediate nodes and can get lost if one of the forwarding nodes fails.
Likewise, forwarding paths can change dynamically and thus cause messages to
Likewise, forwarding paths can change dynamically and thus cause messages to
arrive out of order.
arrive out of order.
The messaging layer of CAF has three primitives for sending messages:
The messaging layer of CAF has three primitives for sending messages: ``send``,
\lstinline^send^, \lstinline^request^, and \lstinline^delegate^. The former
``request``, and ``delegate``. The former simply enqueues a message to the
simply enqueues a message to the mailbox the receiver. The latter two are
mailbox the receiver. The latter two are discussed in more detail in
discussed in more detail in \sref{request} and \sref{delegate}.
:ref:`request` and :ref:`delegate`.
.. _mailbox-element:
\subsection{Structure of Mailbox Elements}
Structure of Mailbox Elements
\label{mailbox-element}
-----------------------------
When enqueuing a message to the mailbox of an actor, CAF wraps the content of
When enqueuing a message to the mailbox of an actor, CAF wraps the content of
the message into a \lstinline^mailbox_element^ (shown below) to add meta data
the message into a ``mailbox_element`` (shown below) to add meta data
and processing paths.
and processing paths.
\singlefig{mailbox_element}{UML class diagram for \lstinline^mailbox_element^}{mailbox_element}
.. _mailbox_element:
.. image:: mailbox_element.png
:alt: UML class diagram for ``mailbox_element``
The sender is stored as a \lstinline^strong_actor_ptr^ \see{actor-pointer} and
The sender is stored as a ``strong_actor_ptr`` (see :ref:`actor-pointer`) and
denotes the origin of the message. The message ID is either 0---invalid---or a
denotes the origin of the message. The message ID is either 0---invalid---or a
positive integer value that allows the sender to match a response to its
positive integer value that allows the sender to match a response to its
request. The \lstinline^stages^ vector stores the path of the message. Response
request. The ``stages`` vector stores the path of the message. Response
messages, i.e., the returned values of a message handler, are sent to
messages, i.e., the returned values of a message handler, are sent to
\lstinline^stages.back()^ after calling \lstinline^stages.pop_back()^. This
``stages.back()`` after calling ``stages.pop_back()``. This allows CAF to build
allows CAF to build pipelines of arbitrary size. If no more stage is left, the
pipelines of arbitrary size. If no more stage is left, the response reaches the
response reaches the sender. Finally, \lstinline^content()^ grants access to
sender. Finally, ``content()`` grants access to the type-erased tuple storing
the type-erased tuple storing the message itself.
the message itself.
Mailbox elements are created by CAF automatically and are usually invisible to
Mailbox elements are created by CAF automatically and are usually invisible to
the programmer. However, understanding how messages are processed internally
the programmer. However, understanding how messages are processed internally
...
@@ -41,237 +48,326 @@ It is worth mentioning that CAF usually wraps the mailbox element and its
...
@@ -41,237 +48,326 @@ It is worth mentioning that CAF usually wraps the mailbox element and its
content into a single object in order to reduce the number of memory
content into a single object in order to reduce the number of memory
allocations.
allocations.
\subsection{Copy on Write}
.. _copy-on-write:
\label{copy-on-write}
Copy on Write
-------------
CAF allows multiple actors to implicitly share message contents, as long as no
CAF allows multiple actors to implicitly share message contents, as long as no
actor performs writes. This allows groups~\see{groups} to send the same content
actor performs writes. This allows groups (see :ref:`groups`) to send the same
to all subscribed actors without any copying overhead.
content to all subscribed actors without any copying overhead.
Actors copy message contents whenever other actors hold references to it and if
Actors copy message contents whenever other actors hold references to it and if
one or more arguments of a message handler take a mutable reference.
one or more arguments of a message handler take a mutable reference.
\subsection{Requirements for Message Types}
Requirements for Message Types
------------------------------
Message types in CAF must meet the following requirements:
Message types in CAF must meet the following requirements:
\begin{enumerate}
1. Serializable or inspectable (see :ref:`type-inspection`)
\item Serializable or inspectable \see{type-inspection}
2. Default constructible
\item Default constructible
3. Copy constructible
\item Copy constructible
\end{enumerate}
A type is serializable if it provides free function
``serialize(Serializer&, T&)`` or
``serialize(Serializer&, T&, const unsigned int)``. Accordingly, a type is
inspectable if it provides a free function ``inspect(Inspector&, T&)``.
Requirement 2 is a consequence of requirement 1, because CAF needs to be able to
create an object of a type before it can call ``serialize`` or ``inspect`` on
it. Requirement 3 allows CAF to implement Copy on Write (see
:ref:`copy-on-write`).
A type is serializable if it provides free function \lstinline^serialize(Serializer&, T&)^ or \lstinline^serialize(Serializer&, T&, const unsigned int)^. Accordingly, a type is inspectable if it provides a free function \lstinline^inspect(Inspector&, T&)^.
.. _special-handler:
Requirement 2 is a consequence of requirement 1, because CAF needs to be able
Default and System Message Handlers
to create an object of a type before it can call \lstinline^serialize^ or
-----------------------------------
\lstinline^inspect^ on it. Requirement 3 allows CAF to implement Copy on
Write~\see{copy-on-write}.
\subsection{Default and System Message Handlers}
CAF has three system-level message types (``down_msg``, ``exit_msg``, and
\label{special-handler}
``error``) that all actor should handle regardless of there current state.
Consequently, event-based actors handle such messages in special-purpose message
handlers. Additionally, event-based actors have a fallback handler for unmatched
messages. Note that blocking actors have neither of those special-purpose
handlers (see :ref:`blocking-actor`).
CAF has three system-level message types (\lstinline^down_msg^,
.. _down-message:
\lstinline^exit_msg^, and \lstinline^error^) that all actor should handle
regardless of there current state. Consequently, event-based actors handle such
messages in special-purpose message handlers. Additionally, event-based actors
have a fallback handler for unmatched messages. Note that blocking actors have
neither of those special-purpose handlers \see{blocking-actor}.
\subsubsection{Down Handler}
Down Handler
\label{down-message}
~~~~~~~~~~~~~
Actors can monitor the lifetime of other actors by calling \lstinline^self->monitor(other)^. This will cause the runtime system of CAF to send a \lstinline^down_msg^ for \lstinline^other^ if it dies. Actors drop down messages unless they provide a custom handler via \lstinline^set_down_handler(f)^, where \lstinline^f^ is a function object with signature \lstinline^void (down_msg&)^ or \lstinline^void (scheduled_actor*, down_msg&)^. The latter signature allows users to implement down message handlers as free function.
Actors can monitor the lifetime of other actors by calling
``self->monitor(other)``. This will cause the runtime system of CAF to send a
``down_msg`` for ``other`` if it dies. Actors drop down messages unless they
provide a custom handler via ``set_down_handler(f)``, where ``f`` is a function
object with signature ``void (down_msg&)`` or
``void (scheduled_actor*, down_msg&)``. The latter signature allows users to
implement down message handlers as free function.
\subsubsection{Exit Handler}
.. _exit-message:
\label{exit-message}
Bidirectional monitoring with a strong lifetime coupling is established by calling \lstinline^self->link_to(other)^. This will cause the runtime to send an \lstinline^exit_msg^ if either \lstinline^this^ or \lstinline^other^ dies. Per default, actors terminate after receiving an \lstinline^exit_msg^ unless the exit reason is \lstinline^exit_reason::normal^. This mechanism propagates failure states in an actor system. Linked actors form a sub system in which an error causes all actors to fail collectively. Actors can override the default handler via \lstinline^set_exit_handler(f)^, where \lstinline^f^ is a function object with signature \lstinline^void (exit_message&)^ or \lstinline^void (scheduled_actor*, exit_message&)^.
Exit Handler
~~~~~~~~~~~~
\subsubsection{Error Handler}
Bidirectional monitoring with a strong lifetime coupling is established by
\label{error-message}
calling ``self->link_to(other)``. This will cause the runtime to send an
``exit_msg`` if either ``this`` or ``other`` dies. Per default, actors terminate
after receiving an ``exit_msg`` unless the exit reason is
``exit_reason::normal``. This mechanism propagates failure states in an actor
system. Linked actors form a sub system in which an error causes all actors to
fail collectively. Actors can override the default handler via
``set_exit_handler(f)``, where ``f`` is a function object with signature
``void (exit_message&)`` or ``void (scheduled_actor*, exit_message&)``.
Actors send error messages to others by returning an \lstinline^error^ \see{error} from a message handler. Similar to exit messages, error messages usually cause the receiving actor to terminate, unless a custom handler was installed via \lstinline^set_error_handler(f)^, where \lstinline^f^ is a function object with signature \lstinline^void (error&)^ or \lstinline^void (scheduled_actor*, error&)^. Additionally, \lstinline^request^ accepts an error handler as second argument to handle errors for a particular request~\see{error-response}. The default handler is used as fallback if \lstinline^request^ is used without error handler.
.. _error-message:
\subsubsection{Default Handler}
Error Handler
\label{default-handler}
~~~~~~~~~~~~~
Actors send error messages to others by returning an ``error`` (see
:ref:`error`) from a message handler. Similar to exit messages, error messages
usually cause the receiving actor to terminate, unless a custom handler was
installed via ``set_error_handler(f)``, where ``f`` is a function object with
signature ``void (error&)`` or ``void (scheduled_actor*, error&)``.
Additionally, ``request`` accepts an error handler as second argument to handle
errors for a particular request (see :ref:`error-response`). The default handler
is used as fallback if ``request`` is used without error handler.
.. _default-handler:
Default Handler
~~~~~~~~~~~~~~~
The default handler is called whenever the behavior of an actor did not match
The default handler is called whenever the behavior of an actor did not match
the input. Actors can change the default handler by calling
the input. Actors can change the default handler by calling
\lstinline^set_default_handler^. The expected signature of the function object
``set_default_handler``. The expected signature of the function object
is \lstinline^result<message> (scheduled_actor*, message_view&)^, whereas the
is ``result<message> (scheduled_actor*, message_view&)``, whereas the
\lstinline^self^ pointer can again be omitted. The default handler can return a
``self`` pointer can again be omitted. The default handler can return a
response message or cause the runtime to \emph{skip} the input message to allow
response message or cause the runtime to *skip* the input message to allow
an actor to handle it in a later state. CAF provides the following built-in
an actor to handle it in a later state. CAF provides the following built-in
\lstinline^print_and_drop^, \lstinline^drop^, and \lstinline^skip^. The former
``print_and_drop``, ``drop``, and ``skip``. The former
two are meant for debugging and testing purposes and allow an actor to simply
two are meant for debugging and testing purposes and allow an actor to simply
return an input. The next two functions drop unexpected messages with or
return an input. The next two functions drop unexpected messages with or
without printing a warning beforehand. Finally, \lstinline^skip^ leaves the
without printing a warning beforehand. Finally, ``skip`` leaves the
input message in the mailbox. The default is \lstinline^print_and_drop^.
input message in the mailbox. The default is ``print_and_drop``.
\subsection{Requests}
.. _request:
\label{request}
Requests
--------
A main feature of CAF is its ability to couple input and output types via the
A main feature of CAF is its ability to couple input and output types via the
type system. For example, a \lstinline^typed_actor<replies_to<int>::with<int>>^
type system. For example, a ``typed_actor<replies_to<int>::with<int>>``
essentially behaves like a function. It receives a single \lstinline^int^ as
essentially behaves like a function. It receives a single ``int`` as
input and responds with another \lstinline^int^. CAF embraces this functional
input and responds with another ``int``. CAF embraces this functional
take on actors by simply creating response messages from the result of message
take on actors by simply creating response messages from the result of message
handlers. This allows CAF to match \emph{request} to \emph{response} messages
handlers. This allows CAF to match *request* to *response* messages
and to provide a convenient API for this style of communication.
and to provide a convenient API for this style of communication.
\subsubsection{Sending Requests and Handling Responses}
.. _handling-response:
\label{handling-response}
Sending Requests and Handling Responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Actors send request messages by calling \lstinline^request(receiver, timeout, content...)^. This function returns an intermediate object that allows an actor to set a one-shot handler for the response message. Event-based actors can use either \lstinline^request(...).then^ or \lstinline^request(...).await^. The former multiplexes the one-shot handler with the regular actor behavior and handles requests as they arrive. The latter suspends the regular actor behavior until all awaited responses arrive and handles requests in LIFO order. Blocking actors always use \lstinline^request(...).receive^, which blocks until the one-shot handler was called. Actors receive a \lstinline^sec::request_timeout^ \see{sec} error message~\see{error-message} if a timeout occurs. Users can set the timeout to \lstinline^infinite^ for unbound operations. This is only recommended if the receiver is running locally.
Actors send request messages by calling ``request(receiver, timeout,
content...)``. This function returns an intermediate object that allows an actor
to set a one-shot handler for the response message. Event-based actors can use
either ``request(...).then`` or ``request(...).await``. The former multiplexes
the one-shot handler with the regular actor behavior and handles requests as
they arrive. The latter suspends the regular actor behavior until all awaited
responses arrive and handles requests in LIFO order. Blocking actors always use
``request(...).receive``, which blocks until the one-shot handler was called.
Actors receive a ``sec::request_timeout`` (see :ref:`sec`) error message (see
:ref:`error-message`) if a timeout occurs. Users can set the timeout to
``infinite`` for unbound operations. This is only recommended if the receiver is
running locally.
In our following example, we use the simple cell actors shown below as
In our following example, we use the simple cell actors shown below as
These classes concern I/O functionality and have thus been moved to
These classes concern I/O functionality and have thus been moved to
\lstinline^caf::io^
``caf::io``
\subsection{0.10 to 0.11}
0.10 to 0.11
------------
Version 0.11 introduced new, optional components. The core library itself,
Version 0.11 introduced new, optional components. The core library itself,
however, mainly received optimizations and bugfixes with one exception: the
however, mainly received optimizations and bugfixes with one exception: the
member function \lstinline^on_exit^ is no longer virtual. You can still provide
member function ``on_exit`` is no longer virtual. You can still provide
it to define a custom exit handler, but you must not use \lstinline^override^.
it to define a custom exit handler, but you must not use ``override``.
\subsection{0.11 to 0.12}
0.11 to 0.12
------------
Version 0.12 removed two features:
Version 0.12 removed two features:
\begin{itemize}
* Type names are no longer demangled automatically. Hence, users must explicitly pass the type name as first argument when using ``announce``, i.e., ``announce<my_class>(...)`` becomes ``announce<my_class>("my_class", ...)``.
\item Type names are no longer demangled automatically. Hence, users must
* Synchronous send blocks no longer support ``continue_with``. This feature has been removed without substitution.
explicitly pass the type name as first argument when using
CAF comes with built-in support for writing unit tests in a domain-specific
CAF comes with built-in support for writing unit tests in a domain-specific
language (DSL). The API looks similar to well-known testing frameworks such as
language (DSL). The API looks similar to well-known testing frameworks such as
...
@@ -8,166 +10,135 @@ actors.
...
@@ -8,166 +10,135 @@ actors.
Our design leverages four main concepts:
Our design leverages four main concepts:
\begin{itemize}
* **Checks** represent single boolean expressions.
\item \textbf{Checks} represent single boolean expressions.
* **Tests** contain one or more checks.
\item \textbf{Tests} contain one or more checks.
* **Fixtures** equip tests with a fixed data environment.
\item \textbf{Fixtures} equip tests with a fixed data environment.
* **Suites** group tests together.
\item \textbf{Suites} group tests together.
\end{itemize}
The following code illustrates a very basic test case that captures the four
The following code illustrates a very basic test case that captures the four
main concepts described above.
main concepts described above.
\begin{lstlisting}
.. code-block:: C++
// Adds all tests in this compilation unit to the suite "math".
#define CAF_SUITE math
// Adds all tests in this compilation unit to the suite "math".
#define CAF_SUITE math
// Pulls in all the necessary macros.
// Pulls in all the necessary macros.
#include "caf/test/dsl.hpp"
#include "caf/test/dsl.hpp"
namespace {
namespace {
struct fixture {};
struct fixture {};
} // namespace
} // namespace
// Makes all members of `fixture` available to tests in the scope.
// Makes all members of `fixture` available to tests in the scope.
CAF_TEST_FIXTURE_SCOPE(math_tests, fixture)
CAF_TEST_FIXTURE_SCOPE(math_tests, fixture)
// Implements our first test.
// Implements our first test.
CAF_TEST(divide) {
CAF_TEST(divide) {
CAF_CHECK(1 / 1 == 0); // fails
CAF_CHECK(1 / 1 == 0); // fails
CAF_CHECK(2 / 2 == 1); // passes
CAF_CHECK(2 / 2 == 1); // passes
CAF_REQUIRE(3 + 3 == 5); // fails and aborts test execution
CAF_REQUIRE(3 + 3 == 5); // fails and aborts test execution
CAF_CHECK(4 - 4 == 0); // unreachable due to previous requirement error
CAF_CHECK(4 - 4 == 0); // unreachable due to previous requirement error
}
}
CAF_TEST_FIXTURE_SCOPE_END()
CAF_TEST_FIXTURE_SCOPE_END()
\end{lstlisting}
The code above highlights the two basic macros \lstinline^CAF_CHECK^ and
The code above highlights the two basic macros ``CAF_CHECK`` and
\lstinline^CAF_REQUIRE^. The former reports failed checks, but allows the test
``CAF_REQUIRE``. The former reports failed checks, but allows the test
to continue on error. The latter stops test execution if the boolean expression
to continue on error. The latter stops test execution if the boolean expression
evaluates to false.
evaluates to false.
The third macro worth mentioning is \lstinline^CAF_FAIL^. It unconditionally
The third macro worth mentioning is ``CAF_FAIL``. It unconditionally
stops test execution with an error message. This is particularly useful for
stops test execution with an error message. This is particularly useful for
stopping program execution after receiving unexpected messages, as we will see
stopping program execution after receiving unexpected messages, as we will see
later.
later.
\subsection{Testing Actors}
Testing Actors
--------------
The following example illustrates how to add an actor system as well as a
The following example illustrates how to add an actor system as well as a
scoped actor to fixtures. This allows spawning of and interacting with actors
scoped actor to fixtures. This allows spawning of and interacting with actors
in a similar way regular programs would. Except that we are using macros such
in a similar way regular programs would. Except that we are using macros such
as \lstinline^CAF_CHECK^ and provide tests rather than implementing a
as ``CAF_CHECK`` and provide tests rather than implementing a
\lstinline^caf_main^.
``caf_main``.
\begin{lstlisting}
.. code-block:: C++
namespace {
namespace {
struct fixture {
caf::actor_system_config cfg;
struct fixture {
caf::actor_system sys;
caf::actor_system_config cfg;
caf::scoped_actor self;
caf::actor_system sys;
caf::scoped_actor self;
fixture() : sys(cfg), self(sys) {
// nop
fixture() : sys(cfg), self(sys) {
}
// nop
};
}
};
caf::behavior adder() {
return {
caf::behavior adder() {
[=](int x, int y) {
return {
return x + y;
[=](int x, int y) {
}
return x + y;
};
}
}
};
}
} // namespace
} // namespace
CAF_TEST_FIXTURE_SCOPE(actor_tests, fixture)
CAF_TEST_FIXTURE_SCOPE(actor_tests, fixture)
CAF_TEST(simple actor test) {
// Our Actor-Under-Test.
CAF_TEST(simple actor test) {
auto aut = self->spawn(adder);
// Our Actor-Under-Test.
self->request(aut, caf::infinite, 3, 4).receive(
auto aut = self->spawn(adder);
[=](int r) {
self->request(aut, caf::infinite, 3, 4).receive(
CAF_CHECK(r == 7);
[=](int r) {
},
CAF_CHECK(r == 7);
[&](caf::error& err) {
},
// Must not happen, stop test.
[&](caf::error& err) {
CAF_FAIL(sys.render(err));
// Must not happen, stop test.
});
CAF_FAIL(sys.render(err));
}
});
}
CAF_TEST_FIXTURE_SCOPE_END()
\end{lstlisting}
CAF_TEST_FIXTURE_SCOPE_END()
The example above works, but suffers from several issues:
The example above works, but suffers from several issues:
\begin{itemize}
* Significant amount of boilerplate code.
* Using a scoped actor as illustrated above can only test one actor at a time. However, messages between other actors are invisible to us.
\item
* CAF runs actors in a thread pool by default. The resulting nondeterminism makes triggering reliable ordering of messages near impossible. Further, forcing timeouts to test error handling code is even harder.
Significant amount of boilerplate code.
\item
Using a scoped actor as illustrated above can only test one actor at a
time. However, messages between other actors are invisible to us.
\item
CAF runs actors in a thread pool by default. The resulting nondeterminism
Deterministic Testing
makes triggering reliable ordering of messages near impossible. Further,
---------------------
forcing timeouts to test error handling code is even harder.
\end{itemize}
\subsection{Deterministic Testing}
CAF provides a scheduler implementation specifically tailored for writing unit
CAF provides a scheduler implementation specifically tailored for writing unit
tests called \lstinline^test_coordinator^. It does not start any threads and
tests called ``test_coordinator``. It does not start any threads and
instead gives unit tests full control over message dispatching and timeout
instead gives unit tests full control over message dispatching and timeout
management.
management.
To reduce boilerplate code, CAF also provides a fixture template called
To reduce boilerplate code, CAF also provides a fixture template called
\lstinline^test_coordinator_fixture^ that comes with ready-to-use actor system
``test_coordinator_fixture`` that comes with ready-to-use actor system
(\lstinline^sys^) and testing scheduler (\lstinline^sched^). The optional
(``sys``) and testing scheduler (``sched``). The optional
template parameter allows unit tests to plugin custom actor system
template parameter allows unit tests to plugin custom actor system
configuration classes.
configuration classes.
Using this fixture unlocks three additional macros:
Using this fixture unlocks three additional macros:
\begin{itemize}
* ``expect`` checks for a single message. The macro verifies the content types of the message and invokes the necessary member functions on the test coordinator. Optionally, the macro checks the receiver of the message and its content. If the expected message does not exist, the test aborts.
* ``allow`` is similar to ``expect``, but it does not abort the test if the expected message is missing. This macro returns ``true`` if the allowed message was delivered, ``false`` otherwise.
\item
* ``disallow`` aborts the test if a particular message was delivered to an actor.
\lstinline^expect^ checks for a single message. The macro verifies the
content types of the message and invokes the necessary member functions on
the test coordinator. Optionally, the macro checks the receiver of the
message and its content. If the expected message does not exist, the test
aborts.
\item
\lstinline^allow^ is similar to \lstinline^expect^, but it does not abort
the test if the expected message is missing. This macro returns
\lstinline^true^ if the allowed message was delivered, \lstinline^false^
otherwise.
\item
\lstinline^disallow^ aborts the test if a particular message was delivered
The following example implements two actors, ``ping`` and
to an actor.
``pong``, that exchange a configurable amount of messages. The test
*three pings* then checks the contents of each message with
``expect`` and verifies that no additional messages exist using