Commit f15f5afc authored by Dominik Charousset's avatar Dominik Charousset

Convert manual to reStructuredText

(cherry picked from commit 5c274689)
parent 4e96bbf2
\section{Actors} .. _actor:
\label{actor}
Actors
======
Actors in CAF are a lightweight abstraction for units of computations. They Actors in CAF are a lightweight abstraction for units of computations. They
are active objects in the sense that they own their state and do not allow are active objects in the sense that they own their state and do not allow
...@@ -13,7 +15,7 @@ using asynchronous event handlers or blocking receives. These three ...@@ -13,7 +15,7 @@ using asynchronous event handlers or blocking receives. These three
characteristics can be combined freely, with one exception: statically typed characteristics can be combined freely, with one exception: statically typed
actors are always event-based. For example, an actor can have dynamically typed actors are always event-based. For example, an actor can have dynamically typed
messaging, implement a class, and use blocking receives. The common base class messaging, implement a class, and use blocking receives. The common base class
for all user-defined actors is called \lstinline^local_actor^. for all user-defined actors is called ``local_actor``.
Dynamically typed actors are more familiar to developers coming from Erlang or Dynamically typed actors are more familiar to developers coming from Erlang or
Akka. They (usually) enable faster prototyping but require extensive unit Akka. They (usually) enable faster prototyping but require extensive unit
...@@ -30,324 +32,347 @@ hundred bytes. Developers can exclude---detach---event-based actors that ...@@ -30,324 +32,347 @@ hundred bytes. Developers can exclude---detach---event-based actors that
potentially starve others from the cooperative scheduling while spawning it. A potentially starve others from the cooperative scheduling while spawning it. A
detached actor lives in its own thread of execution. detached actor lives in its own thread of execution.
\subsection{Environment / Actor Systems} .. _actor-system:
\label{actor-system}
Environment / Actor Systems
---------------------------
All actors live in an \lstinline^actor_system^ representing an actor All actors live in an ``actor_system`` representing an actor environment
environment including scheduler~\see{scheduler}, registry~\see{registry}, and including :ref:`scheduler`, :ref:`registry`, and optional components such as a
optional components such as a middleman~\see{middleman}. A single process can :ref:`middleman`. A single process can have multiple ``actor_system`` instances,
have multiple \lstinline^actor_system^ instances, but this is usually not but this is usually not recommended (a use case for multiple systems is to
recommended (a use case for multiple systems is to strictly separate two or strictly separate two or more sets of actors by running them in different
more sets of actors by running them in different schedulers). For configuration schedulers). For configuration and fine-tuning options of actor systems see
and fine-tuning options of actor systems see \sref{system-config}. A :ref:`system-config`. A distributed CAF application consists of two or more
distributed CAF application consists of two or more connected actor systems. We connected actor systems. We also refer to interconnected ``actor_system``
also refer to interconnected \lstinline^actor_system^ instances as a instances as a *distributed actor system*.
\emph{distributed actor system}.
\clearpage Common Actor Base Types
\subsection{Common Actor Base Types} -----------------------
The following pseudo-UML depicts the class diagram for actors in CAF. The following pseudo-UML depicts the class diagram for actors in CAF.
Irrelevant member functions and classes as well as mixins are omitted for Irrelevant member functions and classes as well as mixins are omitted for
brevity. Selected individual classes are presented in more detail in the brevity. Selected individual classes are presented in more detail in the
following sections. following sections.
\singlefig{actor_types}% .. _actor-types:
{Actor Types in CAF}%
{actor-types} .. image:: actor_types.png
:alt: Actor Types in CAF
\clearpage Class ``local_actor``
\subsubsection{Class \lstinline^local_actor^} ~~~~~~~~~~~~~~~~~~~~~
The class \lstinline^local_actor^ is the root type for all user-defined actors The class ``local_actor`` is the root type for all user-defined actors
in CAF. It defines all common operations. However, users of the library in CAF. It defines all common operations. However, users of the library
usually do not inherit from this class directly. Proper base classes for usually do not inherit from this class directly. Proper base classes for
user-defined actors are \lstinline^event_based_actor^ or user-defined actors are ``event_based_actor`` or
\lstinline^blocking_actor^. The following table also includes member function ``blocking_actor``. The following table also includes member function
inherited from \lstinline^monitorable_actor^ and \lstinline^abstract_actor^. inherited from ``monitorable_actor`` and ``abstract_actor``.
\begin{center} +-------------------------------------+--------------------------------------------------------+
\begin{tabular}{ll} | **Types** | |
\textbf{Types} & ~ \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``mailbox_type`` | A concurrent, many-writers-single-reader queue type. |
\lstinline^mailbox_type^ & A concurrent, many-writers-single-reader queue type. \\ +-------------------------------------+--------------------------------------------------------+
\hline | | |
~ & ~ \\ \textbf{Constructors} & ~ \\ +-------------------------------------+--------------------------------------------------------+
\hline | **Constructors** | |
\lstinline^(actor_config&)^ & Constructs the actor using a config. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``(actor_config&)`` | Constructs the actor using a config. |
~ & ~ \\ \textbf{Observers} & ~ \\ +-------------------------------------+--------------------------------------------------------+
\hline | | |
\lstinline^actor_addr address()^ & Returns the address of this actor. \\ +-------------------------------------+--------------------------------------------------------+
\hline | **Observers** | |
\lstinline^actor_system& system()^ & Returns \lstinline^context()->system()^. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``actor_addr address()`` | Returns the address of this actor. |
\lstinline^actor_system& home_system()^ & Returns the system that spawned this actor. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``actor_system& system()`` | Returns ``context()->system()``. |
\lstinline^execution_unit* context()^ & Returns underlying thread or current scheduler worker. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``actor_system& home_system()`` | Returns the system that spawned this actor. |
~ & ~ \\ \textbf{Customization Points} & ~ \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``execution_unit* context()`` | Returns underlying thread or current scheduler worker. |
\lstinline^on_exit()^ & Can be overridden to perform cleanup code. \\ +-------------------------------------+--------------------------------------------------------+
\hline | | |
\lstinline^const char* name()^ & Returns a debug name for this actor type. \\ +-------------------------------------+--------------------------------------------------------+
\hline | **Customization Points** | |
~ & ~ \\ \textbf{Actor Management} & ~ \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``on_exit()`` | Can be overridden to perform cleanup code. |
\lstinline^link_to(other)^ & Link to an actor \see{link}. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``const char* name()`` | Returns a debug name for this actor type. |
\lstinline^unlink_from(other)^ & Remove link to an actor \see{link}. \\ +-------------------------------------+--------------------------------------------------------+
\hline | | |
\lstinline^monitor(other)^ & Unidirectionally monitors an actor \see{monitor}. \\ +-------------------------------------+--------------------------------------------------------+
\hline | **Actor Management** | |
\lstinline^demonitor(other)^ & Removes a monitor from \lstinline^whom^. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``link_to(other)`` | Links to ``other`` (see :ref:`link`). |
\lstinline^spawn(F fun, xs...)^ & Spawns a new actor from \lstinline^fun^. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``unlink_from(other)`` | Remove the link to ``other``. |
\lstinline^spawn<T>(xs...)^ & Spawns a new actor of type \lstinline^T^. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``monitor(other)`` | Adds a monitor to ``other`` (see :ref:`monitor`). |
~ & ~ \\ \textbf{Message Processing} & ~ \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``demonitor(other)`` | Removes a monitor from ``whom``. |
\lstinline^T make_response_promise<Ts...>()^ & Allows an actor to delay its response message. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``spawn(F fun, xs...)`` | Spawns a new actor from ``fun``. |
\lstinline^T response(xs...)^ & Convenience function for creating fulfilled promises. \\ +-------------------------------------+--------------------------------------------------------+
\hline | ``spawn<T>(xs...)`` | Spawns a new actor of type ``T``. |
\end{tabular} +-------------------------------------+--------------------------------------------------------+
\end{center} | | |
+-------------------------------------+--------------------------------------------------------+
\clearpage | **Message Processing** | |
\subsubsection{Class \lstinline^scheduled_actor^} +-------------------------------------+--------------------------------------------------------+
| ``T make_response_promise<Ts...>()``| Allows an actor to delay its response message. |
All scheduled actors inherit from \lstinline^scheduled_actor^. This includes +-------------------------------------+--------------------------------------------------------+
| ``T response(xs...)`` | Convenience function for creating fulfilled promises. |
+-------------------------------------+--------------------------------------------------------+
Class ``scheduled_actor``
~~~~~~~~~~~~~~~~~~~~~~~~~
All scheduled actors inherit from ``scheduled_actor``. This includes
statically and dynamically typed event-based actors as well as brokers statically and dynamically typed event-based actors as well as brokers
\see{broker}. :ref:`broker`.
\begin{center} +-------------------------------+--------------------------------------------------------------------------+
\begin{tabular}{ll} | **Types** | |
\textbf{Types} & ~ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``pointer`` | ``scheduled_actor*`` |
\lstinline^pointer^ & \lstinline^scheduled_actor*^ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``exception_handler`` | ``function<error (pointer, std::exception_ptr&)>`` |
\lstinline^exception_handler^ & \lstinline^function<error (pointer, std::exception_ptr&)>^ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``default_handler`` | ``function<result<message> (pointer, message_view&)>`` |
\lstinline^default_handler^ & \lstinline^function<result<message> (pointer, message_view&)>^ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``error_handler`` | ``function<void (pointer, error&)>`` |
\lstinline^error_handler^ & \lstinline^function<void (pointer, error&)>^ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``down_handler`` | ``function<void (pointer, down_msg&)>`` |
\lstinline^down_handler^ & \lstinline^function<void (pointer, down_msg&)>^ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``exit_handler`` | ``function<void (pointer, exit_msg&)>`` |
\lstinline^exit_handler^ & \lstinline^function<void (pointer, exit_msg&)>^ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | | |
~ & ~ \\ \textbf{Constructors} & ~ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | **Constructors** | |
\lstinline^(actor_config&)^ & Constructs the actor using a config. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``(actor_config&)`` | Constructs the actor using a config. |
~ & ~ \\ \textbf{Termination} & ~ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | | |
\lstinline^quit()^ & Stops this actor with normal exit reason. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | **Termination** | |
\lstinline^quit(error x)^ & Stops this actor with error \lstinline^x^. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``quit()`` | Stops this actor with normal exit reason. |
~ & ~ \\ \textbf{Special-purpose Handlers} & ~ \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``quit(error x)`` | Stops this actor with error ``x``. |
\lstinline^set_exception_handler(F f)^ & Installs \lstinline^f^ for converting exceptions to errors \see{error}. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | | |
\lstinline^set_down_handler(F f)^ & Installs \lstinline^f^ to handle down messages \see{down-message}. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | **Special-purpose Handlers** | |
\lstinline^set_exit_handler(F f)^ & Installs \lstinline^f^ to handle exit messages \see{exit-message}. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``set_exception_handler(F f)``| Installs ``f`` for converting exceptions to errors (see :ref`error`). |
\lstinline^set_error_handler(F f)^ & Installs \lstinline^f^ to handle error messages (see \sref{error-message} and \sref{error}). \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``set_down_handler(F f)`` | Installs ``f`` to handle down messages (see :ref:`down-message`). |
\lstinline^set_default_handler(F f)^ & Installs \lstinline^f^ as fallback message handler \see{default-handler}. \\ +-------------------------------+--------------------------------------------------------------------------+
\hline | ``set_exit_handler(F f)`` | Installs ``f`` to handle exit messages (see :ref:`exit-message`). |
\end{tabular} +-------------------------------+--------------------------------------------------------------------------+
\end{center} | ``set_error_handler(F f)`` | Installs ``f`` to handle error messages (see :ref:`error-message`). |
+-------------------------------+--------------------------------------------------------------------------+
\clearpage | ``set_default_handler(F f)`` | Installs ``f`` as fallback message handler (see :ref:`default-handler`). |
\subsubsection{Class \lstinline^blocking_actor^} +-------------------------------+--------------------------------------------------------------------------+
Class ``blocking_actor``
~~~~~~~~~~~~~~~~~~~~~~~~
A blocking actor always lives in its own thread of execution. They are not as A blocking actor always lives in its own thread of execution. They are not as
lightweight as event-based actors and thus do not scale up to large numbers. lightweight as event-based actors and thus do not scale up to large numbers.
The primary use case for blocking actors is to use a \lstinline^scoped_actor^ The primary use case for blocking actors is to use a ``scoped_actor``
for ad-hoc communication to selected actors. Unlike scheduled actors, CAF does for ad-hoc communication to selected actors. Unlike scheduled actors, CAF does
\textbf{not} dispatch system messages to special-purpose handlers. A blocking **not** dispatch system messages to special-purpose handlers. A blocking
actors receives \emph{all} messages regularly through its mailbox. A blocking actors receives *all* messages regularly through its mailbox. A blocking
actor is considered \emph{done} only after it returned from \lstinline^act^ (or actor is considered *done* only after it returned from ``act`` (or
from the implementation in function-based actors). A \lstinline^scoped_actor^ from the implementation in function-based actors). A ``scoped_actor``
sends its exit messages as part of its destruction. sends its exit messages as part of its destruction.
\begin{center} +----------------------------------+---------------------------------------------------+
\begin{tabular}{ll} | **Constructors** | |
\textbf{Constructors} & ~ \\ +----------------------------------+---------------------------------------------------+
\hline | ``(actor_config&)`` | Constructs the actor using a config. |
\lstinline^(actor_config&)^ & Constructs the actor using a config. \\ +----------------------------------+---------------------------------------------------+
\hline | | |
~ & ~ \\ \textbf{Customization Points} & ~ \\ +----------------------------------+---------------------------------------------------+
\hline | **Customization Points** | |
\lstinline^void act()^ & Implements the behavior of the actor. \\ +----------------------------------+---------------------------------------------------+
\hline | ``void act()`` | Implements the behavior of the actor. |
~ & ~ \\ \textbf{Termination} & ~ \\ +----------------------------------+---------------------------------------------------+
\hline | | |
\lstinline^const error& fail_state()^ & Returns the current exit reason. \\ +----------------------------------+---------------------------------------------------+
\hline | **Termination** | |
\lstinline^fail_state(error x)^ & Sets the current exit reason. \\ +----------------------------------+---------------------------------------------------+
\hline | ``const error& fail_state()`` | Returns the current exit reason. |
~ & ~ \\ \textbf{Actor Management} & ~ \\ +----------------------------------+---------------------------------------------------+
\hline | ``fail_state(error x)`` | Sets the current exit reason. |
\lstinline^wait_for(Ts... xs)^ & Blocks until all actors \lstinline^xs...^ are done. \\ +----------------------------------+---------------------------------------------------+
\hline | | |
\lstinline^await_all_other_actors_done()^ & Blocks until all other actors are done. \\ +----------------------------------+---------------------------------------------------+
\hline | **Actor Management** | |
~ & ~ \\ \textbf{Message Handling} & ~ \\ +----------------------------------+---------------------------------------------------+
\hline | ``wait_for(Ts... xs)`` | Blocks until all actors ``xs...`` are done. |
\lstinline^receive(Ts... xs)^ & Receives a message using the callbacks \lstinline^xs...^. \\ +----------------------------------+---------------------------------------------------+
\hline | ``await_all_other_actors_done()``| Blocks until all other actors are done. |
\lstinline^receive_for(T& begin, T end)^ & See \sref{receive-loop}. \\ +----------------------------------+---------------------------------------------------+
\hline | | |
\lstinline^receive_while(F stmt)^ & See \sref{receive-loop}. \\ +----------------------------------+---------------------------------------------------+
\hline | **Message Handling** | |
\lstinline^do_receive(Ts... xs)^ & See \sref{receive-loop}. \\ +----------------------------------+---------------------------------------------------+
\hline | ``receive(Ts... xs)`` | Receives a message using the callbacks ``xs...``. |
\end{tabular} +----------------------------------+---------------------------------------------------+
\end{center} | ``receive_for(T& begin, T end)`` | See receive-loop_. |
+----------------------------------+---------------------------------------------------+
\clearpage | ``receive_while(F stmt)`` | See receive-loop_. |
\subsection{Messaging Interfaces} +----------------------------------+---------------------------------------------------+
\label{interface} | ``do_receive(Ts... xs)`` | See receive-loop_. |
+----------------------------------+---------------------------------------------------+
.. _interface:
Messaging Interfaces
--------------------
Statically typed actors require abstract messaging interfaces to allow the Statically typed actors require abstract messaging interfaces to allow the
compiler to type-check actor communication. Interfaces in CAF are defined using compiler to type-check actor communication. Interfaces in CAF are defined using
the variadic template \lstinline^typed_actor<...>^, which defines the proper the variadic template ``typed_actor<...>``, which defines the proper
actor handle at the same time. Each template parameter defines one actor handle at the same time. Each template parameter defines one
\lstinline^input/output^ pair via ``input/output`` pair via
\lstinline^replies_to<X1,...,Xn>::with<Y1,...,Yn>^. For inputs that do not ``replies_to<X1,...,Xn>::with<Y1,...,Yn>``. For inputs that do not
generate outputs, \lstinline^reacts_to<X1,...,Xn>^ can be used as shortcut for generate outputs, ``reacts_to<X1,...,Xn>`` can be used as shortcut for
\lstinline^replies_to<X1,...,Xn>::with<void>^. In the same way functions cannot ``replies_to<X1,...,Xn>::with<void>``. In the same way functions cannot
be overloaded only by their return type, interfaces cannot accept one input be overloaded only by their return type, interfaces cannot accept one input
twice (possibly mapping it to different outputs). The example below defines a twice (possibly mapping it to different outputs). The example below defines a
messaging interface for a simple calculator. messaging interface for a simple calculator.
\cppexample[17-21]{message_passing/calculator} .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 17-18
It is not required to create a type alias such as \lstinline^calculator_actor^, It is not required to create a type alias such as ``calculator_actor``,
but it makes dealing with statically typed actors much easier. Also, a central but it makes dealing with statically typed actors much easier. Also, a central
alias definition eases refactoring later on. alias definition eases refactoring later on.
Interfaces have set semantics. This means the following two type aliases Interfaces have set semantics. This means the following two type aliases
\lstinline^i1^ and \lstinline^i2^ are equal: ``i1`` and ``i2`` are equal:
\begin{lstlisting} .. code-block:: C++
using i1 = typed_actor<replies_to<A>::with<B>, replies_to<C>::with<D>>;
using i2 = typed_actor<replies_to<C>::with<D>, replies_to<A>::with<B>>;
\end{lstlisting}
Further, actor handles of type \lstinline^A^ are assignable to handles of type using i1 = typed_actor<replies_to<A>::with<B>, replies_to<C>::with<D>>;
\lstinline^B^ as long as \lstinline^B^ is a subset of \lstinline^A^. using i2 = typed_actor<replies_to<C>::with<D>, replies_to<A>::with<B>>;
For convenience, the class \lstinline^typed_actor<...>^ defines the member Further, actor handles of type ``A`` are assignable to handles of type
``B`` as long as ``B`` is a subset of ``A``.
For convenience, the class ``typed_actor<...>`` defines the member
types shown below to grant access to derived types. types shown below to grant access to derived types.
\begin{center} +------------------------+---------------------------------------------------------------+
\begin{tabular}{ll} | **Types** | |
\textbf{Types} & ~ \\ +------------------------+---------------------------------------------------------------+
\hline | ``behavior_type`` | A statically typed set of message handlers. |
\lstinline^behavior_type^ & A statically typed set of message handlers. \\ +------------------------+---------------------------------------------------------------+
\hline | ``base`` | Base type for actors, i.e., ``typed_event_based_actor<...>``. |
\lstinline^base^ & Base type for actors, i.e., \lstinline^typed_event_based_actor<...>^. \\ +------------------------+---------------------------------------------------------------+
\hline | ``pointer`` | A pointer of type ``base*``. |
\lstinline^pointer^ & A pointer of type \lstinline^base*^. \\ +------------------------+---------------------------------------------------------------+
\hline | ``stateful_base<T>`` | See stateful-actor_. |
\lstinline^stateful_base<T>^ & See \sref{stateful-actor}. \\ +------------------------+---------------------------------------------------------------+
\hline | ``stateful_pointer<T>``| A pointer of type ``stateful_base<T>*``. |
\lstinline^stateful_pointer<T>^ & A pointer of type \lstinline^stateful_base<T>*^. \\ +------------------------+---------------------------------------------------------------+
\hline | ``extend<Ts...>`` | Extend this typed actor with ``Ts...``. |
\lstinline^extend<Ts...>^ & Extend this typed actor with \lstinline^Ts...^. \\ +------------------------+---------------------------------------------------------------+
\hline | ``extend_with<Other>`` | Extend this typed actor with all cases from ``Other``. |
\lstinline^extend_with<Other>^ & Extend this typed actor with all cases from \lstinline^Other^. \\ +------------------------+---------------------------------------------------------------+
\hline
\end{tabular} .. _spawn:
\end{center}
Spawning Actors
\clearpage ---------------
\subsection{Spawning Actors}
\label{spawn}
Both statically and dynamically typed actors are spawned from an Both statically and dynamically typed actors are spawned from an
\lstinline^actor_system^ using the member function \lstinline^spawn^. The ``actor_system`` using the member function ``spawn``. The
function either takes a function as first argument or a class as first template function either takes a function as first argument or a class as first template
parameter. For example, the following functions and classes represent actors. parameter. For example, the following functions and classes represent actors.
\cppexample[24-29]{message_passing/calculator} .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 21-26
Spawning an actor for each implementation is illustrated below. Spawning an actor for each implementation is illustrated below.
\cppexample[140-145]{message_passing/calculator} .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 123-128
Additional arguments to \lstinline^spawn^ are passed to the constructor of a Additional arguments to ``spawn`` are passed to the constructor of a
class or used as additional function arguments, respectively. In the example class or used as additional function arguments, respectively. In the example
above, none of the three functions takes any argument other than the implicit above, none of the three functions takes any argument other than the implicit
but optional \lstinline^self^ pointer. but optional ``self`` pointer.
.. _function-based:
\subsection{Function-based Actors} Function-based Actors
\label{function-based} ---------------------
When using a function or function object to implement an actor, the first When using a function or function object to implement an actor, the first
argument \emph{can} be used to capture a pointer to the actor itself. The type argument *can* be used to capture a pointer to the actor itself. The type
of this pointer is usually \lstinline^event_based_actor*^ or of this pointer is usually ``event_based_actor*`` or
\lstinline^blocking_actor*^. The proper pointer type for any ``blocking_actor*``. The proper pointer type for any
\lstinline^typed_actor^ handle \lstinline^T^ can be obtained via ``typed_actor`` handle ``T`` can be obtained via
\lstinline^T::pointer^ \see{interface}. ``T::pointer`` interface_.
Blocking actors simply implement their behavior in the function body. The actor Blocking actors simply implement their behavior in the function body. The actor
is done once it returns from that function. is done once it returns from that function.
Event-based actors can either return a \lstinline^behavior^ Event-based actors can either return a ``behavior`` (see :ref:`message-handler`)
\see{message-handler} that is used to initialize the actor or explicitly set that is used to initialize the actor or explicitly set the initial behavior by
the initial behavior by calling \lstinline^self->become(...)^. Due to the calling ``self->become(...)``. Due to the asynchronous, event-based nature of
asynchronous, event-based nature of this kind of actor, the function usually this kind of actor, the function usually returns immediately after setting a
returns immediately after setting a behavior (message handler) for the behavior (message handler) for the *next* incoming message. Hence, variables on
\emph{next} incoming message. Hence, variables on the stack will be out of the stack will be out of scope once a message arrives. Managing state in
scope once a message arrives. Managing state in function-based actors can be function-based actors can be done either via rebinding state with ``become``,
done either via rebinding state with \lstinline^become^, using heap-located using heap-located data referenced via ``std::shared_ptr`` or by using the
data referenced via \lstinline^std::shared_ptr^ or by using the ``stateful *stateful actor* abstraction (see :ref:`stateful-actor`).
actor'' abstraction~\see{stateful-actor}.
The following three functions implement the prototypes shown in spawn_ and
The following three functions implement the prototypes shown in~\sref{spawn} illustrate one blocking actor and two event-based actors (statically and
and illustrate one blocking actor and two event-based actors (statically and
dynamically typed). dynamically typed).
\clearpage .. literalinclude:: /examples/message_passing/calculator.cpp
\cppexample[31-72]{message_passing/calculator} :language: C++
:lines: 28-56
\clearpage .. _class-based:
\subsection{Class-based Actors}
\label{class-based} Class-based Actors
------------------
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
\sref{spawn}. spawn_.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 58-92
\cppexample[74-108]{message_passing/calculator} .. _stateful-actor:
\clearpage Stateful Actors
\subsection{Stateful Actors} ---------------
\label{stateful-actor}
The stateful actor API makes it easy to maintain state in function-based The stateful actor API makes it easy to maintain state in function-based
actors. It is also safer than putting state in member variables, because the actors. It is also safer than putting state in member variables, because the
...@@ -355,248 +380,267 @@ state ceases to exist after an actor is done and is not delayed until the ...@@ -355,248 +380,267 @@ state ceases to exist after an actor is done and is not delayed until the
destructor runs. For example, if two actors hold a reference to each other via destructor runs. For example, if two actors hold a reference to each other via
member variables, they produce a cycle and neither will get destroyed. Using member variables, they produce a cycle and neither will get destroyed. Using
stateful actors instead breaks the cycle, because references are destroyed when stateful actors instead breaks the cycle, because references are destroyed when
an actor calls \lstinline^self->quit()^ (or is killed externally). The an actor calls ``self->quit()`` (or is killed externally). The
following example illustrates how to implement stateful actors with static following example illustrates how to implement stateful actors with static
typing as well as with dynamic typing. typing as well as with dynamic typing.
\cppexample[18-44]{message_passing/cell} .. literalinclude:: /examples/message_passing/cell.cpp
:language: C++
:lines: 18-44
Stateful actors are spawned in the same way as any other function-based actor Stateful actors are spawned in the same way as any other function-based actor
\see{function-based}. function-based_.
\cppexample[49-50]{message_passing/cell} .. literalinclude:: /examples/message_passing/cell.cpp
:language: C++
:lines: 49-50
\clearpage .. _composable-behavior:
\subsection{Actors from Composable Behaviors \experimental}
\label{composable-behavior} Actors from Composable Behaviors :sup:`experimental`
-----------------------------------------------------
When building larger systems, it is often useful to implement the behavior of When building larger systems, it is often useful to implement the behavior of
an actor in terms of other, existing behaviors. The composable behaviors in an actor in terms of other, existing behaviors. The composable behaviors in
CAF allow developers to generate a behavior class from a messaging CAF allow developers to generate a behavior class from a messaging
interface~\see{interface}. interface interface_.
The base type for composable behaviors is \lstinline^composable_behavior<T>^, The base type for composable behaviors is ``composable_behavior<T>``,
where \lstinline^T^ is a \lstinline^typed_actor<...>^. CAF maps each where ``T`` is a ``typed_actor<...>``. CAF maps each
\lstinline^replies_to<A,B,C>::with<D,E,F>^ in \lstinline^T^ to a pure virtual ``replies_to<A,B,C>::with<D,E,F>`` in ``T`` to a pure virtual
member function with signature: member function with signature:
\begin{lstlisting} .. code-block:: C++
result<D, E, F> operator()(param<A>, param<B>, param<C>);.
\end{lstlisting} result<D, E, F> operator()(param<A>, param<B>, param<C>);.
Note that \lstinline^operator()^ will take integral types as well as atom Note that ``operator()`` will take integral types as well as atom constants
constants simply by value. A \lstinline^result<T>^ accepts either a value of simply by value. A ``result<T>`` accepts either a value of type ``T``, a
type \lstinline^T^, a \lstinline^skip_t^ \see{default-handler}, an ``skip_t`` (see :ref:`default-handler`), an ``error`` (see :ref:`error`), a
\lstinline^error^ \see{error}, a \lstinline^delegated<T>^ \see{delegate}, or a ``delegated<T>`` (see :ref:`delegate`), or a ``response_promise<T>`` (see
\lstinline^response_promise<T>^ \see{promise}. A \lstinline^result<void>^ is :ref:`promise`). A ``result<void>`` is constructed by returning ``unit``.
constructed by returning \lstinline^unit^.
A behavior that combines the behaviors \lstinline^X^, \lstinline^Y^, and A behavior that combines the behaviors ``X``, ``Y``, and
\lstinline^Z^ must inherit from \lstinline^composed_behavior<X,Y,Z>^ instead of ``Z`` must inherit from ``composed_behavior<X,Y,Z>`` instead of
inheriting from the three classes directly. The class inheriting from the three classes directly. The class
\lstinline^composed_behavior^ ensures that the behaviors are concatenated ``composed_behavior`` ensures that the behaviors are concatenated
correctly. In case one message handler is defined in multiple base types, the correctly. In case one message handler is defined in multiple base types, the
\emph{first} type in declaration order ``wins''. For example, if \lstinline^X^ *first* type in declaration order wins. For example, if ``X`` and
and \lstinline^Y^ both implement the interface ``Y`` both implement the interface
\lstinline^replies_to<int,int>::with<int>^, only the handler implemented in ``replies_to<int,int>::with<int>``, only the handler implemented in
\lstinline^X^ is active. ``X`` is active.
Any composable (or composed) behavior with no pure virtual member functions can Any composable (or composed) behavior with no pure virtual member functions can
be spawned directly through an actor system by calling be spawned directly through an actor system by calling
\lstinline^system.spawn<...>()^, as shown below. ``system.spawn<...>()``, as shown below.
\cppexample[20-52]{composition/calculator_behavior} .. literalinclude:: /examples/composition/calculator_behavior.cpp
:language: C++
\clearpage :lines: 20-45
The second example illustrates how to use non-primitive values that are wrapped The second example illustrates how to use non-primitive values that are wrapped
in a \lstinline^param<T>^ when working with composable behaviors. The purpose in a ``param<T>`` when working with composable behaviors. The purpose
of \lstinline^param<T>^ is to provide a single interface for both constant and of ``param<T>`` is to provide a single interface for both constant and
non-constant access. Constant access is modeled with the implicit conversion non-constant access. Constant access is modeled with the implicit conversion
operator to a const reference, the member function \lstinline^get()^, and operator to a const reference, the member function ``get()``, and
\lstinline^operator->^. ``operator->``.
When acquiring mutable access to the represented value, CAF copies the value When acquiring mutable access to the represented value, CAF copies the value
before allowing mutable access to it if more than one reference to the value before allowing mutable access to it if more than one reference to the value
exists. This copy-on-write optimization avoids race conditions by design, while exists. This copy-on-write optimization avoids race conditions by design, while
minimizing copy operations \see{copy-on-write}. A mutable reference is returned minimizing copy operations (see :ref:`copy-on-write`). A mutable reference is
from the member functions \lstinline^get_mutable()^ and \lstinline^move()^. The returned from the member functions ``get_mutable()`` and ``move()``. The latter
latter is a convenience function for \lstinline^std::move(x.get_mutable())^. is a convenience function for ``std::move(x.get_mutable())``. The following
The following example illustrates how to use \lstinline^param<std::string>^ example illustrates how to use ``param<std::string>`` when implementing a simple
when implementing a simple dictionary. dictionary.
.. literalinclude:: /examples/composition/dictionary_behavior.cpp
:language: C++
:lines: 22-44
\cppexample[22-44]{composition/dictionary_behavior} .. _attach:
\subsection{Attaching Cleanup Code to Actors} Attaching Cleanup Code to Actors
\label{attach} --------------------------------
Users can attach cleanup code to actors. This code is executed immediately if Users can attach cleanup code to actors. This code is executed immediately if
the actor has already exited. Otherwise, the actor will execute it as part of the actor has already exited. Otherwise, the actor will execute it as part of
its termination. The following example attaches a function object to actors for its termination. The following example attaches a function object to actors for
printing a custom string on exit. printing a custom string on exit.
\cppexample[46-50]{broker/simple_broker} .. literalinclude:: /examples/broker/simple_broker.cpp
:language: C++
:lines: 42-47
It is possible to attach code to remote actors. However, the cleanup code will It is possible to attach code to remote actors. However, the cleanup code will
run on the local machine. run on the local machine.
\subsection{Blocking Actors} .. _blocking-actor:
\label{blocking-actor}
Blocking actors always run in a separate thread and are not scheduled by CAF. Blocking Actors
Unlike event-based actors, blocking actors have explicit, blocking ---------------
\emph{receive} functions. Further, blocking actors do not handle system
messages automatically via special-purpose callbacks \see{special-handler}.
This gives users full control over the behavior of blocking actors. However,
blocking actors still should follow conventions of the actor system. For
example, actors should unconditionally terminate after receiving an
\lstinline^exit_msg^ with reason \lstinline^exit_reason::kill^.
\subsubsection{Receiving Messages} Blocking actors always run in a separate thread and are not scheduled by CAF.
Unlike event-based actors, blocking actors have explicit, blocking *receive*
The function \lstinline^receive^ sequentially iterates over all elements in the functions. Further, blocking actors do not handle system messages automatically
via special-purpose callbacks (see :ref:`special-handler`). This gives users
full control over the behavior of blocking actors. However, blocking actors
still should follow conventions of the actor system. For example, actors should
unconditionally terminate after receiving an ``exit_msg`` with reason
``exit_reason::kill``.
Receiving Messages
~~~~~~~~~~~~~~~~~~
The function ``receive`` sequentially iterates over all elements in the
mailbox beginning with the first. It takes a message handler that is applied to mailbox beginning with the first. It takes a message handler that is applied to
the elements in the mailbox until an element was matched by the handler. An the elements in the mailbox until an element was matched by the handler. An
actor calling \lstinline^receive^ is blocked until it successfully dequeued a actor calling ``receive`` is blocked until it successfully dequeued a
message from its mailbox or an optional timeout occurs. Messages that are not message from its mailbox or an optional timeout occurs. Messages that are not
matched by the behavior are automatically skipped and remain in the mailbox. matched by the behavior are automatically skipped and remain in the mailbox.
\begin{lstlisting} .. code-block:: C++
self->receive (
[](int x) { /* ... */ }
);
\end{lstlisting}
\subsubsection{Catch-all Receive Statements} self->receive (
\label{catch-all} [](int x) { /* ... */ }
);
.. _catch-all:
Catch-all Receive Statements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Blocking actors can use inline catch-all callbacks instead of setting a default Blocking actors can use inline catch-all callbacks instead of setting a default
handler \see{default-handler}. A catch-all case must be the last callback handler (see :ref:`default-handler`). A catch-all case must be the last callback
before the optional timeout, as shown in the example below. before the optional timeout, as shown in the example below.
\begin{lstlisting} .. code-block:: C++
self->receive(
[&](float x) { self->receive(
// ... [&](float x) {
}, // ...
[&](const down_msg& x) { },
// ... [&](const down_msg& x) {
}, // ...
[&](const exit_msg& x) { },
// ... [&](const exit_msg& x) {
}, // ...
others >> [](message_view& x) -> result<message> { },
// report unexpected message back to client others >> [](message_view& x) -> result<message> {
return sec::unexpected_message; // report unexpected message back to client
} return sec::unexpected_message;
); }
\end{lstlisting} );
\clearpage .. _receive-loop:
\subsubsection{Receive Loops}
\label{receive-loop} Receive Loops
~~~~~~~~~~~~~
Message handler passed to \lstinline^receive^ are temporary object at runtime.
Hence, calling \lstinline^receive^ inside a loop creates an unnecessary amount Message handler passed to ``receive`` are temporary object at runtime.
Hence, calling ``receive`` inside a loop creates an unnecessary amount
of short-lived objects. CAF provides predefined receive loops to allow for of short-lived objects. CAF provides predefined receive loops to allow for
more efficient code. more efficient code.
\begin{lstlisting} .. code-block:: C++
// BAD
std::vector<int> results; // BAD
for (size_t i = 0; i < 10; ++i) std::vector<int> results;
receive ( for (size_t i = 0; i < 10; ++i)
[&](int value) { receive (
results.push_back(value); [&](int value) {
} results.push_back(value);
); }
);
// GOOD
std::vector<int> results; // GOOD
size_t i = 0; std::vector<int> results;
receive_for(i, 10) ( size_t i = 0;
[&](int value) { receive_for(i, 10) (
results.push_back(value); [&](int value) {
} results.push_back(value);
); }
\end{lstlisting} );
\begin{lstlisting}
// BAD .. code-block:: C++
size_t received = 0;
while (received < 10) { // BAD
receive ( size_t received = 0;
[&](int) { while (received < 10) {
++received; receive (
} [&](int) {
); ++received;
} ; }
);
// GOOD } ;
size_t received = 0;
receive_while([&] { return received < 10; }) ( // GOOD
[&](int) { size_t received = 0;
++received; receive_while([&] { return received < 10; }) (
} [&](int) {
); ++received;
\end{lstlisting} }
\clearpage );
\begin{lstlisting} .. code-block:: C++
// BAD
size_t received = 0; // BAD
do { size_t received = 0;
receive ( do {
[&](int) { receive (
++received; [&](int) {
} ++received;
); }
} while (received < 10); );
} while (received < 10);
// GOOD
size_t received = 0; // GOOD
do_receive ( size_t received = 0;
[&](int) { do_receive (
++received; [&](int) {
} ++received;
).until([&] { return received >= 10; }); }
\end{lstlisting} ).until([&] { return received >= 10; });
The examples above illustrate the correct usage of the three loops The examples above illustrate the correct usage of the three loops
\lstinline^receive_for^, \lstinline^receive_while^ and ``receive_for``, ``receive_while`` and
\lstinline^do_receive(...).until^. It is possible to nest receives and receive ``do_receive(...).until``. It is possible to nest receives and receive
loops. loops.
\begin{lstlisting} .. code-block:: C++
bool running = true;
self->receive_while([&] { return running; }) ( bool running = true;
[&](int value1) { self->receive_while([&] { return running; }) (
self->receive ( [&](int value1) {
[&](float value2) { self->receive (
aout(self) << value1 << " => " << value2 << endl; [&](float value2) {
} aout(self) << value1 << " => " << value2 << endl;
); }
}, );
// ... },
); // ...
\end{lstlisting} );
\subsubsection{Scoped Actors} .. _scoped-actors:
\label{scoped-actors}
Scoped Actors
The class \lstinline^scoped_actor^ offers a simple way of communicating with ~~~~~~~~~~~~~
CAF actors from non-actor contexts. It overloads \lstinline^operator->^ to
return a \lstinline^blocking_actor*^. Hence, it behaves like the implicit The class ``scoped_actor`` offers a simple way of communicating with
\lstinline^self^ pointer in functor-based actors, only that it ceases to exist CAF actors from non-actor contexts. It overloads ``operator->`` to
return a ``blocking_actor*``. Hence, it behaves like the implicit
``self`` pointer in functor-based actors, only that it ceases to exist
at scope end. at scope end.
\begin{lstlisting} .. code-block:: C++
void test(actor_system& system) {
scoped_actor self{system}; void test(actor_system& system) {
// spawn some actor scoped_actor self{system};
auto aut = self->spawn(my_actor_impl); // spawn some actor
self->send(aut, "hi there"); auto aut = self->spawn(my_actor_impl);
// self will be destroyed automatically here; any self->send(aut, "hi there");
// actor monitoring it will receive down messages etc. // self will be destroyed automatically here; any
} // actor monitoring it will receive down messages etc.
\end{lstlisting} }
\section{Network I/O with Brokers} .. _broker:
\label{broker}
Network I/O with Brokers
========================
When communicating to other services in the network, sometimes low-level socket When communicating to other services in the network, sometimes low-level socket
I/O is inevitable. For this reason, CAF provides \emph{brokers}. A broker is I/O is inevitable. For this reason, CAF provides *brokers*. A broker is
an event-based actor running in the middleman that multiplexes socket I/O. It an event-based actor running in the middleman that multiplexes socket I/O. It
can maintain any number of acceptors and connections. Since the broker runs in can maintain any number of acceptors and connections. Since the broker runs in
the middleman, implementations should be careful to consume as little time as the middleman, implementations should be careful to consume as little time as
possible in message handlers. Brokers should outsource any considerable amount possible in message handlers. Brokers should outsource any considerable amount
of work by spawning new actors or maintaining worker actors. of work by spawning new actors or maintaining worker actors.
\textit{Note that all UDP-related functionality is still \experimental.} *Note that all UDP-related functionality is still :sup:`experimental`.*
\subsection{Spawning Brokers} Spawning Brokers
----------------
Brokers are implemented as functions and are spawned by calling on of the three Brokers are implemented as functions and are spawned by calling on of the three
following member functions of the middleman. following member functions of the middleman.
\begin{lstlisting} .. code-block:: C++
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts> template <spawn_options Os = no_spawn_options,
typename infer_handle_from_fun<F>::type class F = std::function<void(broker*)>, class... Ts>
spawn_broker(F fun, Ts&&... xs); typename infer_handle_from_fun<F>::type
spawn_broker(F fun, Ts&&... xs);
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts> template <spawn_options Os = no_spawn_options,
expected<typename infer_handle_from_fun<F>::type> class F = std::function<void(broker*)>, class... Ts>
spawn_client(F fun, const std::string& host, uint16_t port, Ts&&... xs); expected<typename infer_handle_from_fun<F>::type>
spawn_client(F fun, const std::string& host, uint16_t port, Ts&&... xs);
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts> template <spawn_options Os = no_spawn_options,
expected<typename infer_handle_from_fun<F>::type> class F = std::function<void(broker*)>, class... Ts>
spawn_server(F fun, uint16_t port, Ts&&... xs); expected<typename infer_handle_from_fun<F>::type>
\end{lstlisting} spawn_server(F fun, uint16_t port, Ts&&... xs);
The function \lstinline^spawn_broker^ simply spawns a broker. The convenience The function ``spawn_broker`` simply spawns a broker. The convenience
function \lstinline^spawn_client^ tries to connect to given host and port over function ``spawn_client`` tries to connect to given host and port over
TCP and returns a broker managing this connection on success. Finally, TCP and returns a broker managing this connection on success. Finally,
\lstinline^spawn_server^ opens a local TCP port and spawns a broker managing it ``spawn_server`` opens a local TCP port and spawns a broker managing it
on success. There are no convenience functions spawn a UDP-based client or on success. There are no convenience functions spawn a UDP-based client or
server. server.
\subsection{Class \lstinline^broker^} .. _broker-class:
\label{broker-class}
Class ``broker``
----------------
\begin{lstlisting} .. code-block:: C++
void configure_read(connection_handle hdl, receive_policy::config config);
\end{lstlisting}
Modifies the receive policy for the connection identified by \lstinline^hdl^. void configure_read(connection_handle hdl, receive_policy::config config);
This will cause the middleman to enqueue the next \lstinline^new_data_msg^
according to the given \lstinline^config^ created by Modifies the receive policy for the connection identified by ``hdl``.
\lstinline^receive_policy::exactly(x)^, \lstinline^receive_policy::at_most(x)^, This will cause the middleman to enqueue the next ``new_data_msg``
or \lstinline^receive_policy::at_least(x)^ (with \lstinline^x^ denoting the according to the given ``config`` created by
``receive_policy::exactly(x)``, ``receive_policy::at_most(x)``,
or ``receive_policy::at_least(x)`` (with ``x`` denoting the
number of bytes). number of bytes).
\begin{lstlisting} .. code-block:: C++
void write(connection_handle hdl, size_t num_bytes, const void* buf)
void write(datagram_handle hdl, size_t num_bytes, const void* buf) void write(connection_handle hdl, size_t num_bytes, const void* buf)
\end{lstlisting} void write(datagram_handle hdl, size_t num_bytes, const void* buf)
Writes data to the output buffer. Writes data to the output buffer.
\begin{lstlisting} .. code-block:: C++
void enqueue_datagram(datagram_handle hdl, std::vector<char> buf);
\end{lstlisting} void enqueue_datagram(datagram_handle hdl, std::vector<char> buf);
Enqueues a buffer to be sent as a datagram. Use of this function is encouraged Enqueues a buffer to be sent as a datagram. Use of this function is encouraged
over write as it allows reuse of the buffer which can be returned to the broker over write as it allows reuse of the buffer which can be returned to the broker
in a \lstinline^datagram_sent_msg^. in a ``datagram_sent_msg``.
.. code-block:: C++
\begin{lstlisting} void flush(connection_handle hdl);
void flush(connection_handle hdl); void flush(datagram_handle hdl);
void flush(datagram_handle hdl);
\end{lstlisting}
Sends the data from the output buffer. Sends the data from the output buffer.
\begin{lstlisting} .. code-block:: C++
template <class F, class... Ts>
actor fork(F fun, connection_handle hdl, Ts&&... xs); template <class F, class... Ts>
\end{lstlisting} actor fork(F fun, connection_handle hdl, Ts&&... xs);
Spawns a new broker that takes ownership of a given connection. Spawns a new broker that takes ownership of a given connection.
\begin{lstlisting} .. code-block:: C++
size_t num_connections();
\end{lstlisting} size_t num_connections();
Returns the number of open connections. Returns the number of open connections.
\begin{lstlisting} .. code-block:: C++
void close(connection_handle hdl);
void close(accept_handle hdl); void close(connection_handle hdl);
void close(datagram_handle hdl); void close(accept_handle hdl);
\end{lstlisting} void close(datagram_handle hdl);
Closes the endpoint related to the handle. Closes the endpoint related to the handle.
\begin{lstlisting} .. code-block:: C++
expected<std::pair<accept_handle, uint16_t>>
add_tcp_doorman(uint16_t port = 0, const char* in = nullptr, expected<std::pair<accept_handle, uint16_t>>
bool reuse_addr = false); add_tcp_doorman(uint16_t port = 0, const char* in = nullptr,
\end{lstlisting} bool reuse_addr = false);
Creates new doorman that accepts incoming connections on a given port and Creates new doorman that accepts incoming connections on a given port and
returns the handle to the doorman and the port in use or an error. returns the handle to the doorman and the port in use or an error.
\begin{lstlisting} .. code-block:: C++
expected<connection_handle>
add_tcp_scribe(const std::string& host, uint16_t port); expected<connection_handle>
\end{lstlisting} add_tcp_scribe(const std::string& host, uint16_t port);
Creates a new scribe to connect to host:port and returns handle to it or an Creates a new scribe to connect to host:port and returns handle to it or an
error. error.
\begin{lstlisting} .. code-block:: C++
expected<std::pair<datagram_handle, uint16_t>>
add_udp_datagram_servant(uint16_t port = 0, const char* in = nullptr, expected<std::pair<datagram_handle, uint16_t>>
bool reuse_addr = false); add_udp_datagram_servant(uint16_t port = 0, const char* in = nullptr,
\end{lstlisting} bool reuse_addr = false);
Creates a datagram servant to handle incoming datagrams on a given port. Creates a datagram servant to handle incoming datagrams on a given port.
Returns the handle to the servant and the port in use or an error. Returns the handle to the servant and the port in use or an error.
\begin{lstlisting} .. code-block:: C++
expected<datagram_handle>
add_udp_datagram_servant(const std::string& host, uint16_t port); expected<datagram_handle>
\end{lstlisting} add_udp_datagram_servant(const std::string& host, uint16_t port);
Creates a datagram servant to send datagrams to host:port and returns a handle Creates a datagram servant to send datagrams to host:port and returns a handle
to it or an error. to it or an error.
\subsection{Broker-related Message Types} Broker-related Message Types
----------------------------
Brokers receive system messages directly from the middleman for connection and Brokers receive system messages directly from the middleman for connection and
acceptor events. acceptor events.
\textbf{Note:} brokers are \emph{required} to handle these messages immediately **Note:** brokers are *required* to handle these messages immediately
regardless of their current state. Not handling the system messages from the regardless of their current state. Not handling the system messages from the
broker results in loss of data, because system messages are \emph{not} broker results in loss of data, because system messages are *not*
delivered through the mailbox and thus cannot be skipped. delivered through the mailbox and thus cannot be skipped.
\begin{lstlisting} .. code-block:: C++
struct new_connection_msg {
accept_handle source; struct new_connection_msg {
connection_handle handle; accept_handle source;
}; connection_handle handle;
\end{lstlisting} };
Indicates that \lstinline^source^ accepted a new TCP connection identified by Indicates that ``source`` accepted a new TCP connection identified by
\lstinline^handle^. ``handle``.
\begin{lstlisting} .. code-block:: C++
struct new_data_msg {
connection_handle handle; struct new_data_msg {
std::vector<char> buf; connection_handle handle;
}; std::vector<char> buf;
\end{lstlisting} };
Contains raw bytes received from \lstinline^handle^. The amount of data Contains raw bytes received from ``handle``. The amount of data
received per event is controlled with \lstinline^configure_read^ (see received per event is controlled with ``configure_read`` (see
\ref{broker-class}). It is worth mentioning that the buffer is re-used whenever broker-class_). It is worth mentioning that the buffer is re-used whenever
possible. possible.
\begin{lstlisting} .. code-block:: C++
struct data_transferred_msg {
connection_handle handle; struct data_transferred_msg {
uint64_t written; connection_handle handle;
uint64_t remaining; uint64_t written;
}; uint64_t remaining;
\end{lstlisting} };
This message informs the broker that the \lstinline^handle^ sent This message informs the broker that the ``handle`` sent
\lstinline^written^ bytes with \lstinline^remaining^ bytes in the buffer. Note, ``written`` bytes with ``remaining`` bytes in the buffer. Note,
that these messages are not sent per default but must be explicitly enabled via that these messages are not sent per default but must be explicitly enabled via
the member function \lstinline^ack_writes^. the member function ``ack_writes``.
\begin{lstlisting} .. code-block:: C++
struct connection_closed_msg {
connection_handle handle;
};
struct acceptor_closed_msg { struct connection_closed_msg {
accept_handle handle; connection_handle handle;
}; };
\end{lstlisting}
A \lstinline^connection_closed_msg^ or \lstinline^acceptor_closed_msg^ informs struct acceptor_closed_msg {
accept_handle handle;
};
A ``connection_closed_msg`` or ``acceptor_closed_msg`` informs
the broker that one of its handles is no longer valid. the broker that one of its handles is no longer valid.
\begin{lstlisting} .. code-block:: C++
struct connection_passivated_msg {
connection_handle handle; struct connection_passivated_msg {
}; connection_handle handle;
};
struct acceptor_passivated_msg { struct acceptor_passivated_msg {
accept_handle handle; accept_handle handle;
}; };
\end{lstlisting}
A \lstinline^connection_passivated_msg^ or \lstinline^acceptor_passivated_msg^ A ``connection_passivated_msg`` or ``acceptor_passivated_msg``
informs the broker that one of its handles entered passive mode and no longer informs the broker that one of its handles entered passive mode and no longer
accepts new data or connections \see{trigger}. accepts new data or connections trigger_.
The following messages are related to UDP communication The following messages are related to UDP communication (see
(see~\sref{transport-protocols}. Since UDP is not connection oriented, there is :ref:`transport-protocols`). Since UDP is not connection oriented, there is no
no equivalent to the \lstinline^new_connection_msg^ of TCP. equivalent to the ``new_connection_msg`` of TCP.
\begin{lstlisting} .. code-block:: C++
struct new_datagram_msg {
datagram_handle handle;
network::receive_buffer buf;
};
\end{lstlisting}
Contains the raw bytes from \lstinline^handle^. The buffer always has a maximum struct new_datagram_msg {
datagram_handle handle;
network::receive_buffer buf;
};
Contains the raw bytes from ``handle``. The buffer always has a maximum
size of 65k to receive all regular UDP messages. The amount of bytes can be size of 65k to receive all regular UDP messages. The amount of bytes can be
queried via the \lstinline^.size()^ member function. Similar to TCP, the buffer queried via the ``.size()`` member function. Similar to TCP, the buffer
is reused when possible---please do not resize it. is reused when possible---please do not resize it.
\begin{lstlisting} .. code-block:: C++
struct datagram_sent_msg {
datagram_handle handle; struct datagram_sent_msg {
uint64_t written; datagram_handle handle;
std::vector<char> buf; uint64_t written;
}; std::vector<char> buf;
\end{lstlisting} };
This message informs the broker that the \lstinline^handle^ sent a datagram of This message informs the broker that the ``handle`` sent a datagram of
\lstinline^written^ bytes. It includes the buffer that held the sent message to ``written`` bytes. It includes the buffer that held the sent message to
allow its reuse. Note, that these messages are not sent per default but must be allow its reuse. Note, that these messages are not sent per default but must be
explicitly enabled via the member function \lstinline^ack_writes^. explicitly enabled via the member function ``ack_writes``.
\begin{lstlisting} .. code-block:: C++
struct datagram_servant_closed_msg {
std::vector<datagram_handle> handles;
};
\end{lstlisting}
A \lstinline^datagram_servant_closed_msg^ informs the broker that one of its struct datagram_servant_closed_msg {
std::vector<datagram_handle> handles;
};
A ``datagram_servant_closed_msg`` informs the broker that one of its
handles is no longer valid. handles is no longer valid.
\begin{lstlisting} .. code-block:: C++
struct datagram_servant_passivated_msg {
datagram_handle handle; struct datagram_servant_passivated_msg {
}; datagram_handle handle;
\end{lstlisting} };
A ``datagram_servant_closed_msg`` informs the broker that one of its
handles entered passive mode and no longer accepts new data trigger_.
A \lstinline^datagram_servant_closed_msg^ informs the broker that one of its .. _trigger:
handles entered passive mode and no longer accepts new data \see{trigger}.
\subsection{Manually Triggering Events \experimental} Manually Triggering Events :sup:`experimental`
\label{trigger} -----------------------------------------------
Brokers receive new events as \lstinline^new_connection_msg^ and Brokers receive new events as ``new_connection_msg`` and
\lstinline^new_data_msg^ as soon and as often as they occur, per default. This ``new_data_msg`` as soon and as often as they occur, per default. This
means a fast peer can overwhelm a broker by sending it data faster than the means a fast peer can overwhelm a broker by sending it data faster than the
broker can process it. In particular if the broker outsources work items to broker can process it. In particular if the broker outsources work items to
other actors, because work items can accumulate in the mailboxes of the other actors, because work items can accumulate in the mailboxes of the
workers. workers.
Calling \lstinline^self->trigger(x,y)^, where \lstinline^x^ is a connection or Calling ``self->trigger(x,y)``, where ``x`` is a connection or
acceptor handle and \lstinline^y^ is a positive integer, allows brokers to halt acceptor handle and ``y`` is a positive integer, allows brokers to halt
activities after \lstinline^y^ additional events. Once a connection or acceptor activities after ``y`` additional events. Once a connection or acceptor
stops accepting new data or connections, the broker receives a stops accepting new data or connections, the broker receives a
\lstinline^connection_passivated_msg^ or \lstinline^acceptor_passivated_msg^. ``connection_passivated_msg`` or ``acceptor_passivated_msg``.
Brokers can stop activities unconditionally with \lstinline^self->halt(x)^ and Brokers can stop activities unconditionally with ``self->halt(x)`` and
resume activities unconditionally with \lstinline^self->trigger(x)^. resume activities unconditionally with ``self->trigger(x)``.
\section{Common Pitfalls} .. _pitfalls:
\label{pitfalls}
Common Pitfalls
===============
This Section highlights common mistakes or C++ subtleties that can show up when This Section highlights common mistakes or C++ subtleties that can show up when
programming in CAF. programming in CAF.
\subsection{Defining Message Handlers} Defining Message Handlers
-------------------------
C++ evaluates comma-separated expressions from left-to-right, using only the C++ evaluates comma-separated expressions from left-to-right, using only the
last element as return type of the whole expression. This means that message last element as return type of the whole expression. This means that message
handlers and behaviors must \emph{not} be initialized like this: handlers and behaviors must *not* be initialized like this:
.. code-block:: C++
\begin{lstlisting} message_handler wrong = (
message_handler wrong = ( [](int i) { /*...*/ },
[](int i) { /*...*/ }, [](float f) { /*...*/ }
[](float f) { /*...*/ } );
);
\end{lstlisting}
The correct way to initialize message handlers and behaviors is to either The correct way to initialize message handlers and behaviors is to either
use the constructor or the member function \lstinline^assign^: use the constructor or the member function ``assign``:
\begin{lstlisting} .. code-block:: C++
message_handler ok1{
[](int i) { /*...*/ }, message_handler ok1{
[](float f) { /*...*/ } [](int i) { /*...*/ },
}; [](float f) { /*...*/ }
};
message_handler ok2;
// some place later message_handler ok2;
ok2.assign( // some place later
[](int i) { /*...*/ }, ok2.assign(
[](float f) { /*...*/ } [](int i) { /*...*/ },
); [](float f) { /*...*/ }
\end{lstlisting} );
\subsection{Event-Based API} Event-Based API
---------------
The member function \lstinline^become^ does not block, i.e., always returns
immediately. Thus, lambda expressions should \textit{always} capture by value. The member function ``become`` does not block, i.e., always returns
immediately. Thus, lambda expressions should *always* capture by value.
Otherwise, all references on the stack will cause undefined behavior if the Otherwise, all references on the stack will cause undefined behavior if the
lambda expression is executed. lambda expression is executed.
\subsection{Requests} Requests
--------
A handle returned by \lstinline^request^ represents \emph{exactly one} response A handle returned by ``request`` represents *exactly one* response
message. It is not possible to receive more than one response message. message. It is not possible to receive more than one response message.
The handle returned by \lstinline^request^ is bound to the calling actor. It is The handle returned by ``request`` is bound to the calling actor. It is
not possible to transfer a handle to a response to another actor. not possible to transfer a handle to a response to another actor.
\clearpage Sharing
\subsection{Sharing} -------
It is strongly recommended to \textbf{not} share states between actors. In It is strongly recommended to **not** share states between actors. In
particular, no actor shall ever access member variables or member functions of particular, no actor shall ever access member variables or member functions of
another actor. Accessing shared memory segments concurrently can cause undefined another actor. Accessing shared memory segments concurrently can cause undefined
behavior that is incredibly hard to find and debug. However, sharing behavior that is incredibly hard to find and debug. However, sharing
\textit{data} between actors is fine, as long as the data is \textit{immutable} *data* between actors is fine, as long as the data is *immutable*
and its lifetime is guaranteed to outlive all actors. The simplest way to meet and its lifetime is guaranteed to outlive all actors. The simplest way to meet
the lifetime guarantee is by storing the data in smart pointers such as the lifetime guarantee is by storing the data in smart pointers such as
\lstinline^std::shared_ptr^. Nevertheless, the recommended way of sharing ``std::shared_ptr``. Nevertheless, the recommended way of sharing
informations is message passing. Sending the same message to multiple actors information is message passing. Sending the same message to multiple actors
does not result in copying the data several times. does not result in copying the data several times.
\section{Configuring Actor Applications} .. _system-config:
\label{system-config}
Configuring Actor Applications
==============================
CAF configures applications at startup using an CAF configures applications at startup using an
\lstinline^actor_system_config^ or a user-defined subclass of that type. The ``actor_system_config`` or a user-defined subclass of that type. The
config objects allow users to add custom types, to load modules, and to config objects allow users to add custom types, to load modules, and to
fine-tune the behavior of loaded modules with command line options or fine-tune the behavior of loaded modules with command line options or
configuration files~\see{system-config-options}. configuration files system-config-options_.
The following code example is a minimal CAF application with a :ref:`middleman`
but without any custom configuration options.
The following code example is a minimal CAF application with a .. code-block:: C++
middleman~\see{middleman} but without any custom configuration options.
\begin{lstlisting} void caf_main(actor_system& system) {
void caf_main(actor_system& system) { // ...
// ... }
} CAF_MAIN(io::middleman)
CAF_MAIN(io::middleman)
\end{lstlisting}
The compiler expands this example code to the following. The compiler expands this example code to the following.
\begin{lstlisting} .. code-block:: C++
void caf_main(actor_system& system) {
// ... void caf_main(actor_system& system) {
} // ...
int main(int argc, char** argv) { }
return exec_main<io::middleman>(caf_main, argc, argv); int main(int argc, char** argv) {
} return exec_main<io::middleman>(caf_main, argc, argv);
\end{lstlisting} }
The function \lstinline^exec_main^ creates a config object, loads all modules The function ``exec_main`` creates a config object, loads all modules
requested in \lstinline^CAF_MAIN^ and then calls \lstinline^caf_main^. A requested in ``CAF_MAIN`` and then calls ``caf_main``. A
minimal implementation for \lstinline^main^ performing all these steps manually minimal implementation for ``main`` performing all these steps manually
is shown in the next example for the sake of completeness. is shown in the next example for the sake of completeness.
\begin{lstlisting} .. code-block:: C++
int main(int argc, char** argv) {
actor_system_config cfg; int main(int argc, char** argv) {
// read CLI options actor_system_config cfg;
cfg.parse(argc, argv); // read CLI options
// return immediately if a help text was printed cfg.parse(argc, argv);
if (cfg.cli_helptext_printed) // return immediately if a help text was printed
return 0; if (cfg.cli_helptext_printed)
// load modules return 0;
cfg.load<io::middleman>(); // load modules
// create actor system and call caf_main cfg.load<io::middleman>();
actor_system system{cfg}; // create actor system and call caf_main
caf_main(system); actor_system system{cfg};
} caf_main(system);
\end{lstlisting} }
However, setting up config objects by hand is usually not necessary. CAF However, setting up config objects by hand is usually not necessary. CAF
automatically selects user-defined subclasses of automatically selects user-defined subclasses of
\lstinline^actor_system_config^ if \lstinline^caf_main^ takes a second ``actor_system_config`` if ``caf_main`` takes a second
parameter by reference, as shown in the minimal example below. parameter by reference, as shown in the minimal example below.
\begin{lstlisting} .. code-block:: C++
class my_config : public actor_system_config {
public: class my_config : public actor_system_config {
my_config() { public:
// ... my_config() {
} // ...
}; }
};
void caf_main(actor_system& system, const my_config& cfg) { void caf_main(actor_system& system, const my_config& cfg) {
// ... // ...
} }
CAF_MAIN() CAF_MAIN()
\end{lstlisting}
Users can perform additional initialization, add custom program options, etc. Users can perform additional initialization, add custom program options, etc.
simply by implementing a default constructor. simply by implementing a default constructor.
\subsection{Loading Modules} .. _system-config-module:
\label{system-config-module}
The simplest way to load modules is to use the macro \lstinline^CAF_MAIN^ and Loading Modules
---------------
The simplest way to load modules is to use the macro ``CAF_MAIN`` and
to pass a list of all requested modules, as shown below. to pass a list of all requested modules, as shown below.
\begin{lstlisting} .. code-block:: C++
void caf_main(actor_system& system) {
// ... void caf_main(actor_system& system) {
} // ...
CAF_MAIN(mod1, mod2, ...) }
\end{lstlisting} CAF_MAIN(mod1, mod2, ...)
Alternatively, users can load modules in user-defined config classes. Alternatively, users can load modules in user-defined config classes.
\begin{lstlisting} .. code-block:: C++
class my_config : public actor_system_config {
public: class my_config : public actor_system_config {
my_config() { public:
load<mod1>(); my_config() {
load<mod2>(); load<mod1>();
// ... load<mod2>();
} // ...
}; }
\end{lstlisting} };
The third option is to simply call ``x.load<mod1>()`` on a config
object *before* initializing an actor system with it.
The third option is to simply call \lstinline^x.load<mod1>()^ on a config .. _system-config-options:
object \emph{before} initializing an actor system with it.
\subsection{Command Line Options and INI Configuration Files} Command Line Options and INI Configuration Files
\label{system-config-options} ------------------------------------------------
CAF organizes program options in categories and parses CLI arguments as well CAF organizes program options in categories and parses CLI arguments as well
as INI files. CLI arguments override values in the INI file which override as INI files. CLI arguments override values in the INI file which override
hard-coded defaults. Users can add any number of custom program options by hard-coded defaults. Users can add any number of custom program options by
implementing a subtype of \lstinline^actor_system_config^. The example below implementing a subtype of ``actor_system_config``. The example below
adds three options to the ``global'' category. adds three options to the ``global`` category.
\cppexample[222-234]{remoting/distributed_calculator} .. literalinclude:: /examples/remoting/distributed_calculator.cpp
:language: C++
:lines: 206-218
We create a new ``global'' category in \lstinline^custom_options_}^. Each We create a new ``global`` category in ``custom_options_}``.
following call to \lstinline^add^ then appends individual options to the Each following call to ``add`` then appends individual options to the
category. The first argument to \lstinline^add^ is the associated variable. The category. The first argument to ``add`` is the associated variable. The
second argument is the name for the parameter, optionally suffixed with a second argument is the name for the parameter, optionally suffixed with a
comma-separated single-character short name. The short name is only considered comma-separated single-character short name. The short name is only considered
for CLI parsing and allows users to abbreviate commonly used option names. The for CLI parsing and allows users to abbreviate commonly used option names. The
third and final argument to \lstinline^add^ is a help text. third and final argument to ``add`` is a help text.
The custom \lstinline^config^ class allows end users to set the port for the The custom ``config`` class allows end users to set the port for the
application to 42 with either \lstinline^--port=42^ (long name) or application to 42 with either ``-p 42`` (short name) or
\lstinline^-p 42^ (short name). The long option name is prefixed by the ``--port=42`` (long name). The long option name is prefixed by the
category when using a different category than ``global''. For example, adding category when using a different category than ``global''. For example, adding
the port option to the category ``foo'' means end users have to type the port option to the category ``foo`` means end users have to type
\lstinline^--foo.port=42^ when using the long name. Short names are unaffected ``--foo.port=42`` when using the long name. Short names are unaffected
by the category, but have to be unique. by the category, but have to be unique.
Boolean options do not require arguments. The member variable Boolean options do not require arguments. The member variable
\lstinline^server_mode^ is set to \lstinline^true^ if the command line contains ``server_mode`` is set to ``true`` if the command line contains
either \lstinline^--server-mode^ or \lstinline^-s^. either ``--server-mode`` or ``-s``.
The example uses member variables for capturing user-provided settings for The example uses member variables for capturing user-provided settings for
simplicity. However, this is not required. For example, simplicity. However, this is not required. For example,
\lstinline^add<bool>(...)^ allows omitting the first argument entirely. All ``add<bool>(...)`` allows omitting the first argument entirely. All
values of the configuration are accessible with \lstinline^get_or^. Note that values of the configuration are accessible with ``get_or``. Note that
all global options can omit the \lstinline^"global."^ prefix. all global options can omit the ``"global."`` prefix.
CAF adds the program options ``help'' (with short names \lstinline^-h^ and CAF adds the program options ``help`` (with short names ``-h``
\lstinline^-?^) as well as ``long-help'' to the ``global'' category. and ``-?``) as well as ``long-help`` to the ``global``
category.
The default name for the INI file is \lstinline^caf-application.ini^. Users can
change the file name and path by passing \lstinline^--config-file=<path>^ on The default name for the INI file is ``caf-application.ini``. Users can
the command line. change the file name and path by passing ``--config-file=<path>`` on the
command line.
INI files are organized in categories. No value is allowed outside of a
category (no implicit ``global'' category). The parses uses the following INI files are organized in categories. No value is allowed outside of a category
syntax: (no implicit ``global`` category). The parses uses the following syntax:
\begin{tabular}{p{0.3\textwidth}p{0.65\textwidth}} +------------------------+-----------------------------+
\lstinline^key=true^ & is a boolean \\ | ``key=true`` | is a boolean |
\lstinline^key=1^ & is an integer \\ +------------------------+-----------------------------+
\lstinline^key=1.0^ & is an floating point number \\ | ``key=1`` | is an integer |
\lstinline^key=1ms^ & is an timespan \\ +------------------------+-----------------------------+
\lstinline^key='foo'^ & is an atom \\ | ``key=1.0`` | is an floating point number |
\lstinline^key="foo"^ & is a string \\ +------------------------+-----------------------------+
\lstinline^key=[0, 1, ...]^ & is as a list \\ | ``key=1ms`` | is an timespan |
\lstinline^key={a=1, b=2, ...}^ & is a dictionary (map) \\ +------------------------+-----------------------------+
\end{tabular} | ``key='foo'`` | is an atom |
+------------------------+-----------------------------+
| ``key="foo"`` | is a string |
+------------------------+-----------------------------+
| ``key=[0, 1, ...]`` | is as a list |
+------------------------+-----------------------------+
| ``key={a=1, b=2, ...}``| is a dictionary (map) |
+------------------------+-----------------------------+
The following example INI file lists all standard options in CAF and their The following example INI file lists all standard options in CAF and their
default value. Note that some options such as \lstinline^scheduler.max-threads^ default value. Note that some options such as ``scheduler.max-threads``
are usually detected at runtime and thus have no hard-coded default. are usually detected at runtime and thus have no hard-coded default.
\clearpage .. literalinclude:: /examples/caf-application.ini
\iniexample{caf-application} :language: INI
\clearpage .. _add-custom-message-type:
\subsection{Adding Custom Message Types}
\label{add-custom-message-type}
CAF requires serialization support for all of its message types Adding Custom Message Types
\see{type-inspection}. However, CAF also needs a mapping of unique type names ---------------------------
CAF requires serialization support for all of its message types (see
:ref:`type-inspection`). However, CAF also needs a mapping of unique type names
to user-defined types at runtime. This is required to deserialize arbitrary to user-defined types at runtime. This is required to deserialize arbitrary
messages from the network. messages from the network.
As an introductory example, we (again) use the following POD type As an introductory example, we (again) use the following POD type
\lstinline^foo^. ``foo``.
\cppexample[24-27]{custom_type/custom_types_1} .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 24-27
To make \lstinline^foo^ serializable, we make it inspectable To make ``foo`` serializable, we make it inspectable:
\see{type-inspection}:
\cppexample[30-34]{custom_type/custom_types_1} .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 30-34
Finally, we give \lstinline^foo^ a platform-neutral name and add it to the list Finally, we give ``foo`` a platform-neutral name and add it to the list
of serializable types by using a custom config class. of serializable types by using a custom config class.
\cppexample[75-78,81-84]{custom_type/custom_types_1} .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 75-78,81-84
\subsection{Adding Custom Error Types} Adding Custom Error Types
-------------------------
Adding a custom error type to the system is a convenience feature to allow Adding a custom error type to the system is a convenience feature to allow
improve the string representation. Error types can be added by implementing a improve the string representation. Error types can be added by implementing a
render function and passing it to \lstinline^add_error_category^, as shown render function and passing it to ``add_error_category``, as shown in
in~\sref{custom-error}. :ref:`custom-error`.
.. _add-custom-actor-type:
\clearpage Adding Custom Actor Types :sup:`experimental`
\subsection{Adding Custom Actor Types \experimental} ----------------------------------------------
\label{add-custom-actor-type}
Adding actor types to the configuration allows users to spawn actors by their Adding actor types to the configuration allows users to spawn actors by their
name. In particular, this enables spawning of actors on a different node name. In particular, this enables spawning of actors on a different node (see
\see{remote-spawn}. For our example configuration, we consider the following :ref:`remote-spawn`). For our example configuration, we consider the following
simple \lstinline^calculator^ actor. simple ``calculator`` actor.
\cppexample[33-39]{remoting/remote_spawn} .. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 33-34
Adding the calculator actor type to our config is achieved by calling Adding the calculator actor type to our config is achieved by calling
\lstinline^add_actor_type<T>^. Note that adding an actor type in this way ``add_actor_type<T>``. Note that adding an actor type in this way
implicitly calls \lstinline^add_message_type<T>^ for typed actors implicitly calls ``add_message_type<T>`` for typed actors
\see{add-custom-message-type}. This makes our \lstinline^calculator^ actor type add-custom-message-type_. This makes our ``calculator`` actor type
serializable and also enables remote nodes to spawn calculators anywhere in the serializable and also enables remote nodes to spawn calculators anywhere in the
distributed actor system (assuming all nodes use the same config). distributed actor system (assuming all nodes use the same config).
\cppexample[99-101,106-106,110-101]{remoting/remote_spawn} .. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 98-109
Our final example illustrates how to spawn a \lstinline^calculator^ locally by Our final example illustrates how to spawn a ``calculator`` locally by
using its type name. Because the dynamic type name lookup can fail and the using its type name. Because the dynamic type name lookup can fail and the
construction arguments passed as message can mismatch, this version of construction arguments passed as message can mismatch, this version of
\lstinline^spawn^ returns \lstinline^expected<T>^. ``spawn`` returns ``expected<T>``.
\begin{lstlisting} .. code-block:: C++
auto x = system.spawn<calculator>("calculator", make_message());
if (! x) { auto x = system.spawn<calculator>("calculator", make_message());
std::cerr << "*** unable to spawn calculator: " if (! x) {
<< system.render(x.error()) << std::endl; std::cerr << "*** unable to spawn calculator: "
return; << system.render(x.error()) << std::endl;
} return;
calculator c = std::move(*x); }
\end{lstlisting} calculator c = std::move(*x);
Adding dynamically typed actors to the config is achieved in the same way. When Adding dynamically typed actors to the config is achieved in the same way. When
spawning a dynamically typed actor in this way, the template parameter is spawning a dynamically typed actor in this way, the template parameter is
simply \lstinline^actor^. For example, spawning an actor "foo" which requires simply ``actor``. For example, spawning an actor "foo" which requires
one string is created with: one string is created with:
\begin{lstlisting} .. code-block:: C++
auto worker = system.spawn<actor>("foo", make_message("bar"));
\end{lstlisting} auto worker = system.spawn<actor>("foo", make_message("bar"));
Because constructor (or function) arguments for spawning the actor are stored Because constructor (or function) arguments for spawning the actor are stored
in a \lstinline^message^, only actors with appropriate input types are allowed. in a ``message``, only actors with appropriate input types are allowed.
For example, pointer types are illegal. Hence users need to replace C-strings For example, pointer types are illegal. Hence users need to replace C-strings
with \lstinline^std::string^. with ``std::string``.
.. _log-output:
\clearpage Log Output
\subsection{Log Output} ----------
\label{log-output}
Logging is disabled in CAF per default. It can be enabled by setting the Logging is disabled in CAF per default. It can be enabled by setting the
\lstinline^--with-log-level=^ option of the \lstinline^configure^ script to one ``--with-log-level=`` option of the ``configure`` script to one
of ``error'', ``warning'', ``info'', ``debug'', or ``trace'' (from least output of ``error``, ``warning``, ``info``, ``debug``,
to most). Alternatively, setting the CMake variable \lstinline^CAF_LOG_LEVEL^ or ``trace`` (from least output to most). Alternatively, setting the
to 0, 1, 2, 3, or 4 (from least output to most) has the same effect. CMake variable ``CAF_LOG_LEVEL`` to one of these values has the same
effect.
All logger-related configuration options listed here and in All logger-related configuration options listed here and in
\sref{system-config-options} are silently ignored if logging is disabled. system-config-options_ are silently ignored if logging is disabled.
\subsubsection{File Name} .. _log-output-file-name:
\label{log-output-file-name}
File Name
~~~~~~~~~
The output file is generated from the template configured by The output file is generated from the template configured by
\lstinline^logger-file-name^. This template supports the following variables. ``logger-file-name``. This template supports the following variables.
\begin{tabular}{|p{0.2\textwidth}|p{0.75\textwidth}|} +----------------+--------------------------------+
\hline | **Variable** | **Output** |
\textbf{Variable} & \textbf{Output} \\ +----------------+--------------------------------+
\hline | ``[PID]`` | The OS-specific process ID. |
\texttt{[PID]} & The OS-specific process ID. \\ +----------------+--------------------------------+
\hline | ``[TIMESTAMP]``| The UNIX timestamp on startup. |
\texttt{[TIMESTAMP]} & The UNIX timestamp on startup. \\ +----------------+--------------------------------+
\hline | ``[NODE]`` | The node ID of the CAF system. |
\texttt{[NODE]} & The node ID of the CAF system. \\ +----------------+--------------------------------+
\hline
\end{tabular} .. _log-output-console:
\subsubsection{Console} Console
\label{log-output-console} ~~~~~~~
Console output is disabled per default. Setting \lstinline^logger-console^ to Console output is disabled per default. Setting ``logger-console`` to
either \lstinline^"uncolored"^ or \lstinline^"colored"^ prints log events to either ``"uncolored"`` or ``"colored"`` prints log events to
\lstinline^std::clog^. Using the \lstinline^"colored"^ option will print the ``std::clog``. Using the ``"colored"`` option will print the
log events in different colors depending on the severity level. log events in different colors depending on the severity level.
\subsubsection{Format Strings} .. _log-output-format-strings:
\label{log-output-format-strings}
Format Strings
~~~~~~~~~~~~~~
CAF uses log4j-like format strings for configuring printing of individual CAF uses log4j-like format strings for configuring printing of individual
events via \lstinline^logger-file-format^ and events via ``logger-file-format`` and
\lstinline^logger-console-format^. Note that format modifiers are not supported ``logger-console-format``. Note that format modifiers are not supported
at the moment. The recognized field identifiers are: at the moment. The recognized field identifiers are:
\begin{tabular}{|p{0.1\textwidth}|p{0.85\textwidth}|} +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | **Character**| **Output** |
\textbf{Character} & \textbf{Output} \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``c`` | The category/component. |
\texttt{c} & The category/component. This name is defined by the macro \lstinline^CAF_LOG_COMPONENT^. Set this macro before including any CAF header. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``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^. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``d`` | The date in ISO 8601 format, i.e., ``"YYYY-MM-DDThh:mm:ss"``. |
\texttt{d} & The date in ISO 8601 format, i.e., \lstinline^"YYYY-MM-DD hh:mm:ss"^. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``F`` | The file name. |
\texttt{F} & The file name. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``L`` | The line number. |
\texttt{L} & The line number. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``m`` | The user-defined log message. |
\texttt{m} & The user-defined log message. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``M`` | The name of the current function. For example, the name of ``void ns::foo::bar()`` is printed as ``bar``. |
\texttt{M} & The name of the current function. For example, the name of \lstinline^void ns::foo::bar()^ is printed as \lstinline^bar^. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``n`` | A newline. |
\texttt{n} & A newline. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``p`` | The priority (severity level). |
\texttt{p} & The priority (severity level). \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``r`` | Elapsed time since starting the application in milliseconds. |
\texttt{r} & Elapsed time since starting the application in milliseconds. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``t`` | ID of the current thread. |
\texttt{t} & ID of the current thread. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``a`` | ID of the current actor (or ``actor0`` when not logging inside an actor). |
\texttt{a} & ID of the current actor (or ``actor0'' when not logging inside an actor). \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline | ``%`` | A single percent sign. |
\texttt{\%} & A single percent sign. \\ +--------------+-----------------------------------------------------------------------------------------------------------------------------+
\hline
\end{tabular} .. _log-output-filtering:
\subsubsection{Filtering} Filtering
\label{log-output-filtering} ~~~~~~~~~
The two configuration options \lstinline^logger-component-filter^ and The two configuration options ``logger-component-filter`` and
\lstinline^logger-verbosity^ reduce the amount of generated log events. The ``logger-verbosity`` reduce the amount of generated log events. The
former is a list of excluded component names and the latter can increase the former is a list of excluded component names and the latter can increase the
reported severity level (but not decrease it beyond the level defined at reported severity level (but not decrease it beyond the level defined at
compile time). compile time).
\section{Errors} .. _error:
\label{error}
Errors
Errors in CAF have a code and a category, similar to ======
\lstinline^std::error_code^ and \lstinline^std::error_condition^. Unlike its
counterparts from the C++ standard library, \lstinline^error^ is Errors in CAF have a code and a category, similar to ``std::error_code`` and
plattform-neutral and serializable. Instead of using category singletons, CAF ``std::error_condition``. Unlike its counterparts from the C++ standard library,
stores categories as atoms~\see{atom}. Errors can also include a message to ``error`` is plattform-neutral and serializable. Instead of using category
provide additional context information. singletons, CAF stores categories as atoms (see :ref:`atom`). Errors can also
include a message to provide additional context information.
\subsection{Class Interface}
Class Interface
\begin{center} ---------------
\begin{tabular}{ll}
\textbf{Constructors} & ~ \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | **Constructors** | |
\lstinline^(Enum x)^ & Construct error by calling \lstinline^make_error(x)^ \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | ``(Enum x)`` | Construct error by calling ``make_error(x)`` |
\lstinline^(uint8_t x, atom_value y)^ & Construct error with code \lstinline^x^ and category \lstinline^y^ \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` |
\lstinline^(uint8_t x, atom_value y, message z)^ & Construct error with code \lstinline^x^, category \lstinline^y^, and context \lstinline^z^ \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | ``(uint8_t x, atom_value y, message z)``| Construct error with code ``x``, category ``y``, and context ``z`` |
~ & ~ \\ \textbf{Observers} & ~ \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | | |
\lstinline^uint8_t code()^ & Returns the error code \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | **Observers** | |
\lstinline^atom_value category()^ & Returns the error category \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | ``uint8_t code()`` | Returns the error code |
\lstinline^message context()^ & Returns additional context information \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | ``atom_value category()`` | Returns the error category |
\lstinline^explicit operator bool()^ & Returns \lstinline^code() != 0^ \\ +-----------------------------------------+--------------------------------------------------------------------+
\hline | ``message context()`` | Returns additional context information |
\end{tabular} +-----------------------------------------+--------------------------------------------------------------------+
\end{center} | ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+
\subsection{Add Custom Error Categories}
\label{custom-error} .. _custom-error:
Adding custom error categories requires three steps: (1)~declare an enum class Add Custom Error Categories
of type \lstinline^uint8_t^ with the first value starting at 1, (2)~implement a ---------------------------
free function \lstinline^make_error^ that converts the enum to an
\lstinline^error^ object, (3)~add the custom category to the actor system with Adding custom error categories requires three steps: (1) declare an enum class
a render function. The last step is optional to allow users to retrieve a of type ``uint8_t`` with the first value starting at 1, (2) specialize
better string representation from \lstinline^system.render(x)^ than ``error_category`` to give your type a custom ID (value 0-99 are
\lstinline^to_string(x)^ can offer. Note that any error code with value 0 is reserved by CAF), and (3) add your error category to the actor system config.
interpreted as \emph{not-an-error}. The following example adds a custom error The following example adds custom error codes for math errors.
category by performing the first two steps.
.. literalinclude:: /examples/message_passing/divider.cpp
\cppexample[19-34]{message_passing/divider} :language: C++
:lines: 17-47
The implementation of \lstinline^to_string(error)^ is unable to call string
conversions for custom error categories. Hence, .. _sec:
\lstinline^to_string(make_error(math_error::division_by_zero))^ returns
\lstinline^"error(1, math)"^. System Error Codes
------------------
The following code adds a rendering function to the actor system to provide a
more satisfactory string conversion. System Error Codes (SECs) use the error category ``"system"``. They
\cppexample[50-58]{message_passing/divider}
With the custom rendering function,
\lstinline^system.render(make_error(math_error::division_by_zero))^ returns
\lstinline^"math_error(division_by_zero)"^.
\clearpage
\subsection{System Error Codes}
\label{sec}
System Error Codes (SECs) use the error category \lstinline^"system"^. They
represent errors in the actor system or one of its modules and are defined as represent errors in the actor system or one of its modules and are defined as
follows. follows.
\sourcefile[32-117]{libcaf_core/caf/sec.hpp} .. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++
:lines: 32-117
%\clearpage .. _exit-reason:
\subsection{Default Exit Reasons}
\label{exit-reason}
CAF uses the error category \lstinline^"exit"^ for default exit reasons. These Default Exit Reasons
errors are usually fail states set by the actor system itself. The two --------------------
exceptions are \lstinline^exit_reason::user_shutdown^ and
\lstinline^exit_reason::kill^. The former is used in CAF to signalize orderly, CAF uses the error category ``"exit"`` for default exit reasons. These errors
user-requested shutdown and can be used by programmers in the same way. The are usually fail states set by the actor system itself. The two exceptions are
latter terminates an actor unconditionally when used in \lstinline^send_exit^, ``exit_reason::user_shutdown`` and ``exit_reason::kill``. The former is used in
even if the default handler for exit messages~\see{exit-message} is overridden. CAF to signalize orderly, user-requested shutdown and can be used by programmers
in the same way. The latter terminates an actor unconditionally when used in
``send_exit``, even if the default handler for exit messages (see
:ref:`exit-message`) is overridden.
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp
:language: C++
:lines: 29-49
\sourcefile[29-49]{libcaf_core/caf/exit_reason.hpp}
\section{Frequently Asked Questions} .. _faq:
\label{faq}
Frequently Asked Questions
==========================
This Section is a compilation of the most common questions via GitHub, chat, This Section is a compilation of the most common questions via GitHub, chat,
and mailing list. and mailing list.
\subsection{Can I Encrypt CAF Communication?} Can I Encrypt CAF Communication?
--------------------------------
Yes, by using the OpenSSL module~\see{free-remoting-functions}. Yes, by using the OpenSSL module (see :ref:`free-remoting-functions`).
\subsection{Can I Create Messages Dynamically?} Can I Create Messages Dynamically?
----------------------------------
Yes. Yes.
Usually, messages are created implicitly when sending messages but can also be Usually, messages are created implicitly when sending messages but can also be
created explicitly using \lstinline^make_message^. In both cases, types and created explicitly using ``make_message``. In both cases, types and number of
number of elements are known at compile time. To allow for fully dynamic elements are known at compile time. To allow for fully dynamic message
message generation, CAF also offers \lstinline^message_builder^: generation, CAF also offers ``message_builder``:
\begin{lstlisting} .. code-block:: C++
message_builder mb;
// prefix message with some atom message_builder mb;
mb.append(strings_atom::value); // prefix message with some atom
// fill message with some strings mb.append(strings_atom::value);
std::vector<std::string> strings{/*...*/}; // fill message with some strings
for (auto& str : strings) std::vector<std::string> strings{/*...*/};
mb.append(str); for (auto& str : strings)
// create the message mb.append(str);
message msg = mb.to_message(); // create the message
\end{lstlisting} message msg = mb.to_message();
\subsection{What Debugging Tools Exist?} What Debugging Tools Exist?
---------------------------
The \lstinline^scripts/^ and \lstinline^tools/^ directories contain some useful
tools to aid in development and debugging. The ``scripts/`` and ``tools/`` directories contain some useful tools to aid in
development and debugging.
\lstinline^scripts/atom.py^ converts integer atom values back into strings.
``scripts/atom.py`` converts integer atom values back into strings.
\lstinline^scripts/demystify.py^ replaces cryptic \lstinline^typed_mpi<...>^
templates with \lstinline^replies_to<...>::with<...>^ and ``scripts/demystify.py`` replaces cryptic ``typed_mpi<...>``
\lstinline^atom_constant<...>^ with a human-readable representation of the templates with ``replies_to<...>::with<...>`` and
``atom_constant<...>`` with a human-readable representation of the
actual atom. actual atom.
\lstinline^scripts/caf-prof^ is an R script that generates plots from CAF ``scripts/caf-prof`` is an R script that generates plots from CAF
profiler output. profiler output.
\lstinline^caf-vec^ is a (highly) experimental tool that annotates CAF logs ``caf-vec`` is a (highly) experimental tool that annotates CAF logs
with vector timestamps. It gives you happens-before relations and a nice with vector timestamps. It gives you happens-before relations and a nice
visualization via \href{https://bestchai.bitbucket.io/shiviz/}{ShiViz}. visualization via `ShiViz <https://bestchai.bitbucket.io/shiviz/>`_.
\section{Group Communication} .. _groups:
\label{groups}
Group Communication
===================
CAF supports publish/subscribe-based group communication. Dynamically typed CAF supports publish/subscribe-based group communication. Dynamically typed
actors can join and leave groups and send messages to groups. The following actors can join and leave groups and send messages to groups. The following
example showcases the basic API for retrieving a group from a module by its example showcases the basic API for retrieving a group from a module by its
name, joining, and leaving. name, joining, and leaving.
\begin{lstlisting} .. code-block:: C++
std::string module = "local";
std::string id = "foo"; std::string module = "local";
auto expected_grp = system.groups().get(module, id); std::string id = "foo";
if (! expected_grp) { auto expected_grp = system.groups().get(module, id);
std::cerr << "*** cannot load group: " if (! expected_grp) {
<< system.render(expected_grp.error()) << std::endl; std::cerr << "*** cannot load group: "
return; << system.render(expected_grp.error()) << std::endl;
} return;
auto grp = std::move(*expected_grp); }
scoped_actor self{system}; auto grp = std::move(*expected_grp);
self->join(grp); scoped_actor self{system};
self->send(grp, "test"); self->join(grp);
self->receive( self->send(grp, "test");
[](const std::string& str) { self->receive(
assert(str == "test"); [](const std::string& str) {
} assert(str == "test");
); }
self->leave(grp); );
\end{lstlisting} self->leave(grp);
It is worth mentioning that the module \lstinline`"local"` is guaranteed to It is worth mentioning that the module ``"local"`` is guaranteed to
never return an error. The example above uses the general API for retrieving never return an error. The example above uses the general API for retrieving
the group. However, local modules can be easier accessed by calling the group. However, local modules can be easier accessed by calling
\lstinline`system.groups().get_local(id)`, which returns \lstinline`group` ``system.groups().get_local(id)``, which returns ``group``
instead of \lstinline`expected<group>`. instead of ``expected<group>``.
.. _anonymous-group:
\subsection{Anonymous Groups} Anonymous Groups
\label{anonymous-group} ----------------
Groups created on-the-fly with \lstinline^system.groups().anonymous()^ can be Groups created on-the-fly with ``system.groups().anonymous()`` can be
used to coordinate a set of workers. Each call to this function returns a new, used to coordinate a set of workers. Each call to this function returns a new,
unique group instance. unique group instance.
\subsection{Local Groups} .. _local-group:
\label{local-group}
The \lstinline^"local"^ group module creates groups for in-process Local Groups
------------
The ``"local"`` group module creates groups for in-process
communication. For example, a group for GUI related events could be identified communication. For example, a group for GUI related events could be identified
by \lstinline^system.groups().get_local("GUI events")^. The group ID by ``system.groups().get_local("GUI events")``. The group ID
\lstinline^"GUI events"^ uniquely identifies a singleton group instance of the ``"GUI events"`` uniquely identifies a singleton group instance of the
module \lstinline^"local"^. module ``"local"``.
.. _remote-group:
\subsection{Remote Groups} Remote Groups
\label{remote-group} -------------
Calling\lstinline^system.middleman().publish_local_groups(port, addr)^ makes Calling``system.middleman().publish_local_groups(port, addr)`` makes
all local groups available to other nodes in the network. The first argument all local groups available to other nodes in the network. The first argument
denotes the port, while the second (optional) parameter can be used to denotes the port, while the second (optional) parameter can be used to
whitelist IP addresses. whitelist IP addresses.
After publishing the group at one node (the server), other nodes (the clients) After publishing the group at one node (the server), other nodes (the clients)
can get a handle for that group by using the ``remote'' module: can get a handle for that group by using the ``remote'' module:
\lstinline^system.groups().get("remote", "<group>@<host>:<port>")^. This ``system.groups().get("remote", "<group>@<host>:<port>")``. This
implementation uses N-times unicast underneath and the group is only available implementation uses N-times unicast underneath and the group is only available
as long as the hosting server is alive. as long as the hosting server is alive.
\section{Introduction} Introduction
============
Before diving into the API of CAF, we discuss the concepts behind it and Before diving into the API of CAF, we discuss the concepts behind it and
explain the terminology used in this manual. explain the terminology used in this manual.
\subsection{Actor Model} Actor Model
-----------
The actor model describes concurrent entities---actors---that do not share The actor model describes concurrent entities---actors---that do not share
state and communicate only via asynchronous message passing. Decoupling state and communicate only via asynchronous message passing. Decoupling
...@@ -27,10 +29,11 @@ and from one node to many nodes. However, the actor model has not yet been ...@@ -27,10 +29,11 @@ and from one node to many nodes. However, the actor model has not yet been
widely adopted in the native programming domain. With CAF, we contribute a widely adopted in the native programming domain. With CAF, we contribute a
library for actor programming in C++ as open-source software to ease native library for actor programming in C++ as open-source software to ease native
development of concurrent as well as distributed systems. In this regard, CAF development of concurrent as well as distributed systems. In this regard, CAF
follows the C++ philosophy ``building the highest abstraction possible without follows the C++ philosophy *building the highest abstraction possible
sacrificing performance''. without sacrificing performance*.
\subsection{Terminology} Terminology
-----------
CAF is inspired by other implementations based on the actor model such as CAF is inspired by other implementations based on the actor model such as
Erlang or Akka. It aims to provide a modern C++ API allowing for type-safe as Erlang or Akka. It aims to provide a modern C++ API allowing for type-safe as
...@@ -38,7 +41,8 @@ well as dynamically typed messaging. While there are similarities to other ...@@ -38,7 +41,8 @@ well as dynamically typed messaging. While there are similarities to other
implementations, we made many different design decisions that lead to slight implementations, we made many different design decisions that lead to slight
differences when comparing CAF to other actor frameworks. differences when comparing CAF to other actor frameworks.
\subsubsection{Dynamically Typed Actor} Dynamically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~~
A dynamically typed actor accepts any kind of message and dispatches on its A dynamically typed actor accepts any kind of message and dispatches on its
content dynamically at the receiver. This is the ``traditional'' messaging content dynamically at the receiver. This is the ``traditional'' messaging
...@@ -46,7 +50,8 @@ style found in implementations like Erlang or Akka. The upside of this approach ...@@ -46,7 +50,8 @@ style found in implementations like Erlang or Akka. The upside of this approach
is (usually) faster prototyping and less code. This comes at the cost of is (usually) faster prototyping and less code. This comes at the cost of
requiring excessive testing. requiring excessive testing.
\subsubsection{Statically Typed Actor} Statically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~
CAF achieves static type-checking for actors by defining abstract messaging CAF achieves static type-checking for actors by defining abstract messaging
interfaces. Since interfaces define both input and output types, CAF is able to interfaces. Since interfaces define both input and output types, CAF is able to
...@@ -55,75 +60,87 @@ higher robustness to code changes and fewer possible runtime errors. This comes ...@@ -55,75 +60,87 @@ higher robustness to code changes and fewer possible runtime errors. This comes
at an increase in required source code, as developers have to define and use at an increase in required source code, as developers have to define and use
messaging interfaces. messaging interfaces.
\subsubsection{Actor References} .. _actor-reference:
\label{actor-reference}
Actor References
~~~~~~~~~~~~~~~~
CAF uses reference counting for actors. The three ways to store a reference to CAF uses reference counting for actors. The three ways to store a reference to
an actor are addresses, handles, and pointers. Note that \emph{address} does an actor are addresses, handles, and pointers. Note that *address* does
not refer to a \emph{memory region} in this context. not refer to a *memory region* in this context.
.. _actor-address:
\paragraph{Address} Address
\label{actor-address} +++++++
Each actor has a (network-wide) unique logical address. This identifier is Each actor has a (network-wide) unique logical address. This identifier is
represented by \lstinline^actor_addr^, which allows to identify and monitor an represented by ``actor_addr``, which allows to identify and monitor an actor.
actor. Unlike other actor frameworks, CAF does \emph{not} allow users to send Unlike other actor frameworks, CAF does *not* allow users to send messages to
messages to addresses. This limitation is due to the fact that the address does addresses. This limitation is due to the fact that the address does not contain
not contain any type information. Hence, it would not be safe to send it a any type information. Hence, it would not be safe to send it a message, because
message, because the receiving actor might use a statically typed interface the receiving actor might use a statically typed interface that does not accept
that does not accept the given message. Because an \lstinline^actor_addr^ fills the given message. Because an ``actor_addr`` fills the role of an identifier, it
the role of an identifier, it has \emph{weak reference semantics} has *weak reference semantics* (see :ref:`reference-counting`).
\see{reference-counting}.
.. _actor-handle:
\paragraph{Handle}
\label{actor-handle} Handle
++++++
An actor handle contains the address of an actor along with its type
information and is required for sending messages to actors. The distinction An actor handle contains the address of an actor along with its type information
between handles and addresses---which is unique to CAF when comparing it to and is required for sending messages to actors. The distinction between handles
other actor systems---is a consequence of the design decision to enforce static and addresses---which is unique to CAF when comparing it to other actor
type checking for all messages. Dynamically typed actors use \lstinline^actor^ systems---is a consequence of the design decision to enforce static type
handles, while statically typed actors use \lstinline^typed_actor<...>^ checking for all messages. Dynamically typed actors use ``actor`` handles, while
handles. Both types have \emph{strong reference semantics} statically typed actors use ``typed_actor<...>`` handles. Both types have
\see{reference-counting}. *strong reference semantics* (see :ref:`reference-counting`).
\paragraph{Pointer} .. _actor-pointer:
\label{actor-pointer}
Pointer
In a few instances, CAF uses \lstinline^strong_actor_ptr^ to refer to an actor +++++++
using \emph{strong reference semantics} \see{reference-counting} without
knowing the proper handle type. Pointers must be converted to a handle via In a few instances, CAF uses ``strong_actor_ptr`` to refer to an actor using
\lstinline^actor_cast^ \see{actor-cast} prior to sending messages. A *strong reference semantics* (see :ref:`reference-counting`) without knowing the
\lstinline^strong_actor_ptr^ can be \emph{null}. proper handle type. Pointers must be converted to a handle via ``actor_cast``
(see :ref:`actor-cast`) prior to sending messages. A ``strong_actor_ptr`` can be
\subsubsection{Spawning} *null*.
\emph{Spawning} an actor means to create and run a new actor. Spawning
~~~~~~~~
\subsubsection{Monitor}
\label{monitor} *Spawning* an actor means to create and run a new actor.
A monitored actor sends a down message~\see{down-message} to all actors .. _monitor:
Monitor
~~~~~~~
A monitored actor sends a down message (see :ref:`down-message`) to all actors
monitoring it as part of its termination. This allows actors to supervise other monitoring it as part of its termination. This allows actors to supervise other
actors and to take actions when one of the supervised actors fails, i.e., actors and to take actions when one of the supervised actors fails, i.e.,
terminates with a non-normal exit reason. terminates with a non-normal exit reason.
\subsubsection{Link} .. _link:
\label{link}
Link
~~~~
A link is a bidirectional connection between two actors. Each actor sends an A link is a bidirectional connection between two actors. Each actor sends an
exit message~\see{exit-message} to all of its links as part of its termination. exit message (see :ref:`exit-message`) to all of its links as part of its
Unlike down messages, exit messages cause the receiving actor to terminate as termination. Unlike down messages, exit messages cause the receiving actor to
well when receiving a non-normal exit reason per default. This allows terminate as well when receiving a non-normal exit reason per default. This
developers to create a set of actors with the guarantee that either all or no allows developers to create a set of actors with the guarantee that either all
actors are alive. Actors can override the default handler to implement error or no actors are alive. Actors can override the default handler to implement
recovery strategies. error recovery strategies.
\subsection{Experimental Features} Experimental Features
---------------------
Sections that discuss experimental features are highlighted with \experimental.
The API of such features is not stable. This means even minor updates to CAF Sections that discuss experimental features are highlighted with
can come with breaking changes to the API or even remove a feature completely. :sup:`experimental`. The API of such features is not stable. This means even
However, we encourage developers to extensively test such features and to start minor updates to CAF can come with breaking changes to the API or even remove a
discussions to uncover flaws, report bugs, or tweaking the API in order to feature completely. However, we encourage developers to extensively test such
improve a feature or streamline it to cover certain use cases. features and to start discussions to uncover flaws, report bugs, or tweaking the
API in order to improve a feature or streamline it to cover certain use cases.
\section{Managing Groups of Workers \experimental} .. _worker-groups:
\label{worker-groups}
Managing Groups of Workers :sup:`experimental`
===============================================
When managing a set of workers, a central actor often dispatches requests to a When managing a set of workers, a central actor often dispatches requests to a
set of workers. For this purpose, the class \lstinline^actor_pool^ implements a set of workers. For this purpose, the class ``actor_pool`` implements a
lightweight abstraction for managing a set of workers using a dispatching lightweight abstraction for managing a set of workers using a dispatching
policy. Unlike groups, pools usually own their workers. policy. Unlike groups, pools usually own their workers.
Pools are created using the static member function \lstinline^make^, which Pools are created using the static member function ``make``, which
takes either one argument (the policy) or three (number of workers, factory takes either one argument (the policy) or three (number of workers, factory
function for workers, and dispatching policy). After construction, one can add function for workers, and dispatching policy). After construction, one can add
new workers via messages of the form \texttt{('SYS', 'PUT', worker)}, remove new workers via messages of the form ``('SYS', 'PUT', worker)``, remove
workers with \texttt{('SYS', 'DELETE', worker)}, and retrieve the set of workers with ``('SYS', 'DELETE', worker)``, and retrieve the set of
workers as \lstinline^vector<actor>^ via \texttt{('SYS', 'GET')}. workers as ``vector<actor>`` via ``('SYS', 'GET')``.
An actor pool takes ownership of its workers. When forced to quit, it sends an An actor pool takes ownership of its workers. When forced to quit, it sends an
exit messages to all of its workers, forcing them to quit as well. The pool exit messages to all of its workers, forcing them to quit as well. The pool
...@@ -22,61 +24,62 @@ Consequently, a terminating worker loses all unprocessed messages. For more ...@@ -22,61 +24,62 @@ Consequently, a terminating worker loses all unprocessed messages. For more
advanced caching strategies, such as reliable message delivery, users can advanced caching strategies, such as reliable message delivery, users can
implement their own dispatching policies. implement their own dispatching policies.
\subsection{Dispatching Policies} Dispatching Policies
--------------------
A dispatching policy is a functor with the following signature: A dispatching policy is a functor with the following signature:
\begin{lstlisting} .. code-block:: C++
using uplock = upgrade_lock<detail::shared_spinlock>;
using policy = std::function<void (actor_system& sys, using uplock = upgrade_lock<detail::shared_spinlock>;
uplock& guard, using policy = std::function<void (actor_system& sys,
const actor_vec& workers, uplock& guard,
mailbox_element_ptr& ptr, const actor_vec& workers,
execution_unit* host)>; mailbox_element_ptr& ptr,
\end{lstlisting} execution_unit* host)>;
The argument \lstinline^guard^ is a shared lock that can be upgraded for unique The argument ``guard`` is a shared lock that can be upgraded for unique
access if the policy includes a critical section. The second argument is a access if the policy includes a critical section. The second argument is a
vector containing all workers managed by the pool. The argument \lstinline^ptr^ vector containing all workers managed by the pool. The argument ``ptr``
contains the full message as received by the pool. Finally, \lstinline^host^ is contains the full message as received by the pool. Finally, ``host`` is
the current scheduler context that can be used to enqueue workers into the the current scheduler context that can be used to enqueue workers into the
corresponding job queue. corresponding job queue.
The actor pool class comes with a set predefined policies, accessible via The actor pool class comes with a set predefined policies, accessible via
factory functions, for convenience. factory functions, for convenience.
\begin{lstlisting} .. code-block:: C++
actor_pool::policy actor_pool::round_robin();
\end{lstlisting} actor_pool::policy actor_pool::round_robin();
This policy forwards incoming requests in a round-robin manner to workers. This policy forwards incoming requests in a round-robin manner to workers.
There is no guarantee that messages are consumed, i.e., work items are lost if There is no guarantee that messages are consumed, i.e., work items are lost if
the worker exits before processing all of its messages. the worker exits before processing all of its messages.
\begin{lstlisting} .. code-block:: C++
actor_pool::policy actor_pool::broadcast();
\end{lstlisting}
This policy forwards \emph{each} message to \emph{all} workers. Synchronous actor_pool::policy actor_pool::broadcast();
This policy forwards *each* message to *all* workers. Synchronous
messages to the pool will be received by all workers, but the client will only messages to the pool will be received by all workers, but the client will only
recognize the first arriving response message---or error---and discard recognize the first arriving response message---or error---and discard
subsequent messages. Note that this is not caused by the policy itself, but a subsequent messages. Note that this is not caused by the policy itself, but a
consequence of forwarding synchronous messages to more than one actor. consequence of forwarding synchronous messages to more than one actor.
\begin{lstlisting} .. code-block:: C++
actor_pool::policy actor_pool::random();
\end{lstlisting} actor_pool::policy actor_pool::random();
This policy forwards incoming requests to one worker from the pool chosen This policy forwards incoming requests to one worker from the pool chosen
uniformly at random. Analogous to \lstinline^round_robin^, this policy does not uniformly at random. Analogous to ``round_robin``, this policy does not
cache or redispatch messages. cache or redispatch messages.
\begin{lstlisting} .. code-block:: C++
using join = function<void (T&, message&)>;
using split = function<void (vector<pair<actor, message>>&, message&)>; using join = function<void (T&, message&)>;
template <class T> using split = function<void (vector<pair<actor, message>>&, message&)>;
static policy split_join(join jf, split sf = ..., T init = T()); template <class T>
\end{lstlisting} static policy split_join(join jf, split sf = ..., T init = T());
This policy models split/join or scatter/gather work flows, where a work item This policy models split/join or scatter/gather work flows, where a work item
is split into as many tasks as workers are available and then the individuals is split into as many tasks as workers are available and then the individuals
...@@ -84,7 +87,7 @@ results are joined together before sending the full result back to the client. ...@@ -84,7 +87,7 @@ results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together The join function is responsible for ``glueing'' all result messages together
to create a single result. The function is called with the result object to create a single result. The function is called with the result object
(initialed using \lstinline^init^) and the current result messages from a (initialed using ``init``) and the current result messages from a
worker. worker.
The first argument of a split function is a mapping from actors (workers) to The first argument of a split function is a mapping from actors (workers) to
......
\section{Message Handlers} .. _message-handler:
\label{message-handler}
Message Handlers
================
Actors can store a set of callbacks---usually implemented as lambda Actors can store a set of callbacks---usually implemented as lambda
expressions---using either \lstinline^behavior^ or \lstinline^message_handler^. expressions---using either ``behavior`` or ``message_handler``.
The former stores an optional timeout, while the latter is composable. The former stores an optional timeout, while the latter is composable.
\subsection{Definition and Composition} Definition and Composition
--------------------------
As the name implies, a \lstinline^behavior^ defines the response of an actor to As the name implies, a ``behavior`` defines the response of an actor to
messages it receives. The optional timeout allows an actor to dynamically messages it receives. The optional timeout allows an actor to dynamically
change its behavior when not receiving message after a certain amount of time. change its behavior when not receiving message after a certain amount of time.
\begin{lstlisting} .. code-block:: C++
message_handler x1{
[](int i) { /*...*/ }, message_handler x1{
[](double db) { /*...*/ }, [](int i) { /*...*/ },
[](int a, int b, int c) { /*...*/ } [](double db) { /*...*/ },
}; [](int a, int b, int c) { /*...*/ }
\end{lstlisting} };
In our first example, \lstinline^x1^ models a behavior accepting messages that In our first example, ``x1`` models a behavior accepting messages that consist
consist of either exactly one \lstinline^int^, or one \lstinline^double^, or of either exactly one ``int``, or one ``double``, or three ``int`` values. Any
three \lstinline^int^ values. Any other message is not matched and gets other message is not matched and gets forwarded to the default handler (see
forwarded to the default handler \see{default-handler}. :ref:`default-handler`).
\begin{lstlisting} .. code-block:: C++
message_handler x2{
[](double db) { /*...*/ }, message_handler x2{
[](double db) { /* - unreachable - */ } [](double db) { /*...*/ },
}; [](double db) { /* - unreachable - */ }
\end{lstlisting} };
Our second example illustrates an important characteristic of the matching Our second example illustrates an important characteristic of the matching
mechanism. Each message is matched against the callbacks in the order they are mechanism. Each message is matched against the callbacks in the order they are
defined. The algorithm stops at the first match. Hence, the second callback in defined. The algorithm stops at the first match. Hence, the second callback in
\lstinline^x2^ is unreachable. ``x2`` is unreachable.
.. code-block:: C++
\begin{lstlisting} message_handler x3 = x1.or_else(x2);
message_handler x3 = x1.or_else(x2); message_handler x4 = x2.or_else(x1);
message_handler x4 = x2.or_else(x1);
\end{lstlisting}
Message handlers can be combined using \lstinline^or_else^. This composition is Message handlers can be combined using ``or_else``. This composition is
not commutative, as our third examples illustrates. The resulting message not commutative, as our third examples illustrates. The resulting message
handler will first try to handle a message using the left-hand operand and will handler will first try to handle a message using the left-hand operand and will
fall back to the right-hand operand if the former did not match. Thus, fall back to the right-hand operand if the former did not match. Thus,
\lstinline^x3^ behaves exactly like \lstinline^x1^. This is because the second ``x3`` behaves exactly like ``x1``. This is because the second
callback in \lstinline^x1^ will consume any message with a single callback in ``x1`` will consume any message with a single
\lstinline^double^ and both callbacks in \lstinline^x2^ are thus unreachable. ``double`` and both callbacks in ``x2`` are thus unreachable.
The handler \lstinline^x4^ will consume messages with a single The handler ``x4`` will consume messages with a single
\lstinline^double^ using the first callback in \lstinline^x2^, essentially ``double`` using the first callback in ``x2``, essentially
overriding the second callback in \lstinline^x1^. overriding the second callback in ``x1``.
\clearpage .. _atom:
\subsection{Atoms}
\label{atom} Atoms
-----
Defining message handlers in terms of callbacks is convenient, but requires a Defining message handlers in terms of callbacks is convenient, but requires a
simple way to annotate messages with meta data. Imagine an actor that provides simple way to annotate messages with meta data. Imagine an actor that provides
...@@ -63,51 +67,51 @@ user-defined operation and returns the result. Without additional context, the ...@@ -63,51 +67,51 @@ user-defined operation and returns the result. Without additional context, the
actor cannot decide whether it should multiply or add the integers. Thus, the actor cannot decide whether it should multiply or add the integers. Thus, the
operation must be encoded into the message. The Erlang programming language operation must be encoded into the message. The Erlang programming language
introduced an approach to use non-numerical constants, so-called introduced an approach to use non-numerical constants, so-called
\textit{atoms}, which have an unambiguous, special-purpose type and do not have *atoms*, which have an unambiguous, special-purpose type and do not have
the runtime overhead of string constants. the runtime overhead of string constants.
Atoms in CAF are mapped to integer values at compile time. This mapping is Atoms in CAF are mapped to integer values at compile time. This mapping is
guaranteed to be collision-free and invertible, but limits atom literals to ten guaranteed to be collision-free and invertible, but limits atom literals to ten
characters and prohibits special characters. Legal characters are characters and prohibits special characters. Legal characters are
\lstinline^_0-9A-Za-z^ and the whitespace character. Atoms are created using ``_0-9A-Za-z`` and the whitespace character. Atoms are created using
the \lstinline^constexpr^ function \lstinline^atom^, as the following example the ``constexpr`` function ``atom``, as the following example
illustrates. illustrates.
\begin{lstlisting} .. code-block:: C++
atom_value a1 = atom("add");
atom_value a2 = atom("multiply"); atom_value a1 = atom("add");
\end{lstlisting} atom_value a2 = atom("multiply");
\textbf{Warning}: The compiler cannot enforce the restrictions at compile time, **Warning**: The compiler cannot enforce the restrictions at compile time,
except for a length check. The assertion \lstinline^atom("!?") != atom("?!")^ except for a length check. The assertion ``atom("!?") != atom("?!")``
is not true, because each invalid character translates to a whitespace is not true, because each invalid character translates to a whitespace
character. character.
While the \lstinline^atom_value^ is computed at compile time, it is not While the ``atom_value`` is computed at compile time, it is not
uniquely typed and thus cannot be used in the signature of a callback. To uniquely typed and thus cannot be used in the signature of a callback. To
accomplish this, CAF offers compile-time \emph{atom constants}. accomplish this, CAF offers compile-time *atom constants*.
.. code-block:: C++
\begin{lstlisting} using add_atom = atom_constant<atom("add")>;
using add_atom = atom_constant<atom("add")>; using multiply_atom = atom_constant<atom("multiply")>;
using multiply_atom = atom_constant<atom("multiply")>;
\end{lstlisting}
Using these constants, we can now define message passing interfaces in a Using these constants, we can now define message passing interfaces in a
convenient way: convenient way:
\begin{lstlisting} .. code-block:: C++
behavior do_math{
[](add_atom, int a, int b) { behavior do_math{
return a + b; [](add_atom, int a, int b) {
}, return a + b;
[](multiply_atom, int a, int b) { },
return a * b; [](multiply_atom, int a, int b) {
} return a * b;
}; }
};
// caller side: send(math_actor, add_atom::value, 1, 2)
\end{lstlisting} // caller side: send(math_actor, add_atom::value, 1, 2)
Atom constants define a static member \lstinline^value^. Please note that this Atom constants define a static member ``value``. Please note that this
static \lstinline^value^ member does \emph{not} have the type static ``value`` member does *not* have the type
\lstinline^atom_value^, unlike \lstinline^std::integral_constant^ for example. ``atom_value``, unlike ``std::integral_constant`` for example.
\section{Message Passing} .. _message-passing:
\label{message-passing}
Message Passing
===============
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
implementations: \lstinline^reflect^, \lstinline^reflect_and_quit^, implementations: ``reflect``, ``reflect_and_quit``,
\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
communication endpoints. communication endpoints.
\cppexample[20-37]{message_passing/request} .. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 20-37
The first part of the example illustrates how event-based actors can use either The first part of the example illustrates how event-based actors can use either
\lstinline^then^ or \lstinline^await^. ``then`` or ``await``.
\cppexample[39-51]{message_passing/request}
\clearpage .. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 39-51
The second half of the example shows a blocking actor making use of The second half of the example shows a blocking actor making use of
\lstinline^receive^. Note that blocking actors have no special-purpose handler ``receive``. Note that blocking actors have no special-purpose handler
for error messages and therefore are required to pass a callback for error for error messages and therefore are required to pass a callback for error
messages when handling response messages. messages when handling response messages.
\cppexample[53-64]{message_passing/request} .. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 53-64
We spawn five cells and assign the values 0, 1, 4, 9, and 16. We spawn five cells and assign the values 0, 1, 4, 9, and 16.
\cppexample[67-69]{message_passing/request} .. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 67-69
When passing the \lstinline^cells^ vector to our three different When passing the ``cells`` vector to our three different
implementations, we observe three outputs. Our \lstinline^waiting_testee^ actor implementations, we observe three outputs. Our ``waiting_testee`` actor
will always print: will always print:
\begin{footnotesize} .. ::
\begin{verbatim}
cell #9 -> 16 cell #9 -> 16
cell #8 -> 9 cell #8 -> 9
cell #7 -> 4 cell #7 -> 4
cell #6 -> 1 cell #6 -> 1
cell #5 -> 0 cell #5 -> 0
\end{verbatim}
\end{footnotesize} This is because ``await`` puts the one-shots handlers onto a stack and
This is because \lstinline^await^ puts the one-shots handlers onto a stack and
enforces LIFO order by re-ordering incoming response messages. enforces LIFO order by re-ordering incoming response messages.
The \lstinline^multiplexed_testee^ implementation does not print its results in The ``multiplexed_testee`` implementation does not print its results in
a predicable order. Response messages arrive in arbitrary order and are handled a predicable order. Response messages arrive in arbitrary order and are handled
immediately. immediately.
Finally, the \lstinline^blocking_testee^ implementation will always print: Finally, the ``blocking_testee`` implementation will always print:
.. ::
\begin{footnotesize} cell #5 -> 0
\begin{verbatim} cell #6 -> 1
cell #5 -> 0 cell #7 -> 4
cell #6 -> 1 cell #8 -> 9
cell #7 -> 4 cell #9 -> 16
cell #8 -> 9
cell #9 -> 16
\end{verbatim}
\end{footnotesize}
Both event-based approaches send all requests, install a series of one-shot Both event-based approaches send all requests, install a series of one-shot
handlers, and then return from the implementing function. In contrast, the handlers, and then return from the implementing function. In contrast, the
blocking function waits for a response before sending another request. blocking function waits for a response before sending another request.
\clearpage Sending Multiple Requests
\subsubsection{Error Handling in Requests} ~~~~~~~~~~~~~~~~~~~~~~~~~
\label{error-response}
Sending the same message to a group of workers is a common work flow in actor
applications. Usually, a manager maintains a set of workers. On request, the
manager fans-out the request to all of its workers and then collects the
results. The function ``fan_out_request`` combined with the merge policy
``select_all`` streamlines this exact use case.
In the following snippet, we have a matrix actor (``self``) that stores
worker actors for each cell (each simply storing an integer). For computing the
average over a row, we use ``fan_out_request``. The result handler
passed to ``then`` now gets called only once with a ``vector``
holding all collected results. Using a response promise promise_ further
allows us to delay responding to the client until we have collected all worker
results.
.. literalinclude:: /examples/message_passing/fan_out_request.cpp
:language: C++
:lines: 86-98
The policy ``select_any`` models a second common use case: sending a
request to multiple receivers but only caring for the first arriving response.
.. _error-response:
Error Handling in Requests
~~~~~~~~~~~~~~~~~~~~~~~~~~
Requests allow CAF to unambiguously correlate request and response messages. Requests allow CAF to unambiguously correlate request and response messages.
This is also true if the response is an error message. Hence, CAF allows to This is also true if the response is an error message. Hence, CAF allows to add
add an error handler as optional second parameter to \lstinline^then^ and an error handler as optional second parameter to ``then`` and ``await`` (this
\lstinline^await^ (this parameter is mandatory for \lstinline^receive^). If no parameter is mandatory for ``receive``). If no such handler is defined, the
such handler is defined, the default error handler \see{error-message} is used default error handler (see :ref:`error-message`) is used as a fallback in
as a fallback in scheduled actors. scheduled actors.
As an example, we consider a simple divider that returns an error on a division As an example, we consider a simple divider that returns an error on a division
by zero. This examples uses a custom error category~\see{error}. by zero. This examples uses a custom error category (see :ref:`error`).
\cppexample[19-25,35-48]{message_passing/divider} .. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:lines: 17-19,49-59
When sending requests to the divider, we use a custom error handlers to report When sending requests to the divider, we use a custom error handlers to report
errors to the user. errors to the user.
\cppexample[68-77]{message_passing/divider} .. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:lines: 70-76
\clearpage .. _delay-message:
\subsection{Delaying Messages}
\label{delay-message}
Messages can be delayed by using the function \lstinline^delayed_send^, as Delaying Messages
-----------------
Messages can be delayed by using the function ``delayed_send``, as
illustrated in the following time-based loop example. illustrated in the following time-based loop example.
\cppexample[56-75]{message_passing/dancing_kirby} .. literalinclude:: /examples/message_passing/dancing_kirby.cpp
:language: C++
:lines: 58-75
.. _delegate:
\clearpage Delegating Messages
\subsection{Delegating Messages} -------------------
\label{delegate}
Actors can transfer responsibility for a request by using \lstinline^delegate^. Actors can transfer responsibility for a request by using ``delegate``.
This enables the receiver of the delegated message to reply as usual---simply This enables the receiver of the delegated message to reply as usual---simply
by returning a value from its message handler---and the original sender of the by returning a value from its message handler---and the original sender of the
message will receive the response. The following diagram illustrates request message will receive the response. The following diagram illustrates request
delegation from actor B to actor C. delegation from actor B to actor C.
\begin{footnotesize} .. ::
\begin{verbatim}
A B C A B C
| | | | | |
| ---(request)---> | | | ---(request)---> | |
| | ---(delegate)--> | | | ---(delegate)--> |
| X |---\ | X |---\
| | | compute | | | compute
| | | result | | | result
| |<--/ | |<--/
| <-------------(reply)-------------- | | <-------------(reply)-------------- |
| X | X
|---\ |---\
| | handle | | handle
| | response | | response
|<--/ |<--/
| |
X X
\end{verbatim}
\end{footnotesize} Returning the result of ``delegate(...)`` from a message handler, as
Returning the result of \lstinline^delegate(...)^ from a message handler, as
shown in the example below, suppresses the implicit response message and allows shown in the example below, suppresses the implicit response message and allows
the compiler to check the result type when using statically typed actors. the compiler to check the result type when using statically typed actors.
\cppexample[15-42]{message_passing/delegating} .. literalinclude:: /examples/message_passing/delegating.cpp
:language: C++
:lines: 15-36
.. _promise:
\subsection{Response Promises} Response Promises
\label{promise} -----------------
Response promises allow an actor to send and receive other messages prior to Response promises allow an actor to send and receive other messages prior to
replying to a particular request. Actors create a response promise using replying to a particular request. Actors create a response promise using
\lstinline^self->make_response_promise<Ts...>()^, where \lstinline^Ts^ is a ``self->make_response_promise<Ts...>()``, where ``Ts`` is a
template parameter pack describing the promised return type. Dynamically typed template parameter pack describing the promised return type. Dynamically typed
actors simply call \lstinline^self->make_response_promise()^. After retrieving actors simply call ``self->make_response_promise()``. After retrieving
a promise, an actor can fulfill it by calling the member function a promise, an actor can fulfill it by calling the member function
\lstinline^deliver(...)^, as shown in the following example. ``deliver(...)``, as shown in the following example.
\cppexample[18-43]{message_passing/promises} .. literalinclude:: /examples/message_passing/promises.cpp
:language: C++
:lines: 18-43
\clearpage Message Priorities
\subsection{Message Priorities} ------------------
By default, all messages have the default priority, i.e., By default, all messages have the default priority, i.e.,
\lstinline^message_priority::normal^. Actors can send urgent messages by ``message_priority::normal``. Actors can send urgent messages by
setting the priority explicitly: setting the priority explicitly:
\lstinline^send<message_priority::high>(dst,...)^. Urgent messages are put into ``send<message_priority::high>(dst,...)``. Urgent messages are put into
a different queue of the receiver's mailbox. Hence, long wait delays can be a different queue of the receiver's mailbox. Hence, long wait delays can be
avoided for urgent communication. avoided for urgent communication.
\section{Type-Erased Tuples, Messages and Message Views} .. _message:
\label{message}
Type-Erased Tuples, Messages and Message Views
==============================================
Messages in CAF are stored in type-erased tuples. The actual message type Messages in CAF are stored in type-erased tuples. The actual message type
itself is usually hidden, as actors use pattern matching to decompose messages itself is usually hidden, as actors use pattern matching to decompose messages
automatically. However, the classes \lstinline^message^ and automatically. However, the classes ``message`` and
\lstinline^message_builder^ allow more advanced use cases than only sending ``message_builder`` allow more advanced use cases than only sending
data from one actor to another. data from one actor to another.
The interface \lstinline^type_erased_tuple^ encapsulates access to arbitrary The interface ``type_erased_tuple`` encapsulates access to arbitrary
data. This data can be stored on the heap or on the stack. A data. This data can be stored on the heap or on the stack. A
\lstinline^message^ is a type-erased tuple that is always heap-allocated and ``message`` is a type-erased tuple that is always heap-allocated and
uses copy-on-write semantics. When dealing with "plain" type-erased tuples, uses copy-on-write semantics. When dealing with "plain" type-erased tuples,
users are required to check if a tuple is referenced by others via users are required to check if a tuple is referenced by others via
\lstinline^type_erased_tuple::shared^ before modifying its content. ``type_erased_tuple::shared`` before modifying its content.
The convenience class \lstinline^message_view^ holds a reference to either a The convenience class ``message_view`` holds a reference to either a
stack-located \lstinline^type_erased_tuple^ or a \lstinline^message^. The stack-located ``type_erased_tuple`` or a ``message``. The
content of the data can be access via \lstinline^message_view::content^ in both content of the data can be access via ``message_view::content`` in both
cases, which returns a \lstinline^type_erased_tuple&^. The content of the view cases, which returns a ``type_erased_tuple&``. The content of the view
can be forced into a message object by calling can be forced into a message object by calling
\lstinline^message_view::move_content_to_message^. This member function either ``message_view::move_content_to_message``. This member function either
returns the stored message object or moves the content of a stack-allocated returns the stored message object or moves the content of a stack-allocated
tuple into a new message. tuple into a new message.
\subsection{RTTI and Type Numbers} RTTI and Type Numbers
---------------------
All builtin types in CAF have a non-zero 6-bit \emph{type number}. All All builtin types in CAF have a non-zero 6-bit *type number*. All
user-defined types are mapped to 0. When querying the run-time type information user-defined types are mapped to 0. When querying the run-time type information
(RTTI) for individual message or tuple elements, CAF returns a pair consisting (RTTI) for individual message or tuple elements, CAF returns a pair consisting
of an integer and a pointer to \lstinline^std::type_info^. The first value is of an integer and a pointer to ``std::type_info``. The first value is
the 6-bit type number. If the type number is non-zero, the second value is a the 6-bit type number. If the type number is non-zero, the second value is a
pointer to the C++ type info, otherwise the second value is null. Additionally, pointer to the C++ type info, otherwise the second value is null. Additionally,
CAF generates 32 bit \emph{type tokens}. These tokens are \emph{type hints} CAF generates 32 bit *type tokens*. These tokens are *type hints*
that summarizes all types in a type-erased tuple. Two type-erased tuples are of that summarizes all types in a type-erased tuple. Two type-erased tuples are of
different type if they have different type tokens (the reverse is not true). different type if they have different type tokens (the reverse is not true).
\clearpage Class ``type_erased_tuple``
\subsection{Class \lstinline^type_erased_tuple^} ---------------------------
\textbf{Note}: Calling modifiers on a shared type-erased tuple is undefined **Note**: Calling modifiers on a shared type-erased tuple is undefined
behavior. behavior.
\begin{center} +----------------------------------------+------------------------------------------------------------+
\begin{tabular}{ll} | **Observers** | |
\textbf{Observers} & ~ \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``bool empty()`` | Returns whether this message is empty. |
\lstinline^bool empty()^ & Returns whether this message is empty. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``size_t size()`` | Returns the size of this message. |
\lstinline^size_t size()^ & Returns the size of this message. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``rtti_pair type(size_t pos)`` | Returns run-time type information for the nth element. |
\lstinline^rtti_pair type(size_t pos)^ & Returns run-time type information for the nth element. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``error save(serializer& x)`` | Writes the tuple to ``x``. |
\lstinline^error save(serializer& x)^ & Writes the tuple to \lstinline^x^. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``error save(size_t n, serializer& x)``| Writes the nth element to ``x``. |
\lstinline^error save(size_t n, serializer& x)^ & Writes the nth element to \lstinline^x^. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``const void* get(size_t n)`` | Returns a const pointer to the nth element. |
\lstinline^const void* get(size_t n)^ & Returns a const pointer to the nth element. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``std::string stringify()`` | Returns a string representation of the tuple. |
\lstinline^std::string stringify()^ & Returns a string representation of the tuple. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``std::string stringify(size_t n)`` | Returns a string representation of the nth element. |
\lstinline^std::string stringify(size_t n)^ & Returns a string representation of the nth element. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``bool matches(size_t n, rtti_pair)`` | Checks whether the nth element has given type. |
\lstinline^bool matches(size_t n, rtti_pair)^ & Checks whether the nth element has given type. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``bool shared()`` | Checks whether more than one reference to the data exists. |
\lstinline^bool shared()^ & Checks whether more than one reference to the data exists. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``bool match_element<T>(size_t n)`` | Checks whether element ``n`` has type ``T``. |
\lstinline^bool match_element<T>(size_t n)^ & Checks whether element \lstinline^n^ has type \lstinline^T^. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``bool match_elements<Ts...>()`` | Checks whether this message has the types ``Ts...``. |
\lstinline^bool match_elements<Ts...>()^ & Checks whether this message has the types \lstinline^Ts...^. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``const T& get_as<T>(size_t n)`` | Returns a const reference to the nth element. |
\lstinline^const T& get_as<T>(size_t n)^ & Returns a const reference to the nth element. \\ +----------------------------------------+------------------------------------------------------------+
\hline | | |
~ & ~ \\ \textbf{Modifiers} & ~ \\ +----------------------------------------+------------------------------------------------------------+
\hline | **Modifiers** | |
\lstinline^void* get_mutable(size_t n)^ & Returns a mutable pointer to the nth element. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``void* get_mutable(size_t n)`` | Returns a mutable pointer to the nth element. |
\lstinline^T& get_mutable_as<T>(size_t n)^ & Returns a mutable reference to the nth element. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``T& get_mutable_as<T>(size_t n)`` | Returns a mutable reference to the nth element. |
\lstinline^void load(deserializer& x)^ & Reads the tuple from \lstinline^x^. \\ +----------------------------------------+------------------------------------------------------------+
\hline | ``void load(deserializer& x)`` | Reads the tuple from ``x``. |
\end{tabular} +----------------------------------------+------------------------------------------------------------+
\end{center}
Class ``message``
\subsection{Class \lstinline^message^} -----------------
The class \lstinline^message^ includes all member functions of The class ``message`` includes all member functions of
\lstinline^type_erased_tuple^. However, calling modifiers is always guaranteed ``type_erased_tuple``. However, calling modifiers is always guaranteed
to be safe. A \lstinline^message^ automatically detaches its content by copying to be safe. A ``message`` automatically detaches its content by copying
it from the shared data on mutable access. The class further adds the following it from the shared data on mutable access. The class further adds the following
member functions over \lstinline^type_erased_tuple^. Note that member functions over ``type_erased_tuple``. Note that
\lstinline^apply^ only detaches the content if a callback takes mutable ``apply`` only detaches the content if a callback takes mutable
references as arguments. references as arguments.
\begin{center} +-----------------------------------------------+------------------------------------------------------------+
\begin{tabular}{ll} | **Observers** | |
\textbf{Observers} & ~ \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message drop(size_t n)`` | Creates a new message with all but the first ``n`` values. |
\lstinline^message drop(size_t n)^ & Creates a new message with all but the first \lstinline^n^ values. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message drop_right(size_t n)`` | Creates a new message with all but the last ``n`` values. |
\lstinline^message drop_right(size_t n)^ & Creates a new message with all but the last \lstinline^n^ values. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message take(size_t n)`` | Creates a new message from the first ``n`` values. |
\lstinline^message take(size_t n)^ & Creates a new message from the first \lstinline^n^ values. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message take_right(size_t n)`` | Creates a new message from the last ``n`` values. |
\lstinline^message take_right(size_t n)^ & Creates a new message from the last \lstinline^n^ values. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message slice(size_t p, size_t n)`` | Creates a new message from ``[p, p + n)``. |
\lstinline^message slice(size_t p, size_t n)^ & Creates a new message from \lstinline^[p, p + n)^. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message extract(message_handler)`` | See extract_. |
\lstinline^message extract(message_handler)^ & See \sref{extract}. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message extract_opts(...)`` | See extract-opts_. |
\lstinline^message extract_opts(...)^ & See \sref{extract-opts}. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | | |
~ & ~ \\ \textbf{Modifiers} & ~ \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | **Modifiers** | |
\lstinline^optional<message> apply(message_handler f)^ & Returns \lstinline^f(*this)^. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``optional<message> apply(message_handler f)``| Returns ``f(*this)``. |
~ & ~ \\ \textbf{Operators} & ~ \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | | |
\lstinline^message operator+(message x, message y)^ & Concatenates \lstinline^x^ and \lstinline^y^. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | **Operators** | |
\lstinline^message& operator+=(message& x, message y)^ & Concatenates \lstinline^x^ and \lstinline^y^. \\ +-----------------------------------------------+------------------------------------------------------------+
\hline | ``message operator+(message x, message y)`` | Concatenates ``x`` and ``y``. |
\end{tabular} +-----------------------------------------------+------------------------------------------------------------+
\end{center} | ``message& operator+=(message& x, message y)``| Concatenates ``x`` and ``y``. |
+-----------------------------------------------+------------------------------------------------------------+
\clearpage
\subsection{Class \texttt{message\_builder}} Class ``message_builder``
-------------------------
\begin{center}
\begin{tabular}{ll} +---------------------------------------------------+-----------------------------------------------------+
\textbf{Constructors} & ~ \\ | **Constructors** | |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^(void)^ & Creates an empty message builder. \\ | ``(void)`` | Creates an empty message builder. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^(Iter first, Iter last)^ & Adds all elements from range \lstinline^[first, last)^. \\ | ``(Iter first, Iter last)`` | Adds all elements from range ``[first, last)``. |
\hline +---------------------------------------------------+-----------------------------------------------------+
~ & ~ \\ \textbf{Observers} & ~ \\ | | |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^bool empty()^ & Returns whether this message is empty. \\ | **Observers** | |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^size_t size()^ & Returns the size of this message. \\ | ``bool empty()`` | Returns whether this message is empty. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^message to_message( )^ & Converts the buffer to an actual message object. \\ | ``size_t size()`` | Returns the size of this message. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^append(T val)^ & Adds \lstinline^val^ to the buffer. \\ | ``message to_message( )`` | Converts the buffer to an actual message object. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^append(Iter first, Iter last)^ & Adds all elements from range \lstinline^[first, last)^. \\ | ``append(T val)`` | Adds ``val`` to the buffer. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^message extract(message_handler)^ & See \sref{extract}. \\ | ``append(Iter first, Iter last)`` | Adds all elements from range ``[first, last)``. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^message extract_opts(...)^ & See \sref{extract-opts}. \\ | ``message extract(message_handler)`` | See extract_. |
\hline +---------------------------------------------------+-----------------------------------------------------+
~ & ~ \\ \textbf{Modifiers} & ~ \\ | ``message extract_opts(...)`` | See extract-opts_. |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^optional<message>^ \lstinline^apply(message_handler f)^ & Returns \lstinline^f(*this)^. \\ | | |
\hline +---------------------------------------------------+-----------------------------------------------------+
\lstinline^message move_to_message()^ & Transfers ownership of its data to the new message. \\ | **Modifiers** | |
\hline +---------------------------------------------------+-----------------------------------------------------+
\end{tabular} | ``optional<message>`` ``apply(message_handler f)``| Returns ``f(*this)``. |
\end{center} +---------------------------------------------------+-----------------------------------------------------+
| ``message move_to_message()`` | Transfers ownership of its data to the new message. |
\clearpage +---------------------------------------------------+-----------------------------------------------------+
\subsection{Extracting}
\label{extract} .. _extract:
The member function \lstinline^message::extract^ removes matched elements from Extracting
----------
The member function ``message::extract`` removes matched elements from
a message. x Messages are filtered by repeatedly applying a message handler to a message. x Messages are filtered by repeatedly applying a message handler to
the greatest remaining slice, whereas slices are generated in the sequence $[0, the greatest remaining slice, whereas slices are generated in the sequence $[0,
size)$, $[0, size-1)$, $...$, $[1, size-1)$, $...$, $[size-1, size)$. Whenever size)$, $[0, size-1)$, $...$, $[1, size-1)$, $...$, $[size-1, size)$. Whenever
...@@ -172,75 +178,76 @@ the same index on the reduced message. ...@@ -172,75 +178,76 @@ the same index on the reduced message.
For example: For example:
\begin{lstlisting} .. code-block:: C++
auto msg = make_message(1, 2.f, 3.f, 4);
// remove float and integer pairs auto msg = make_message(1, 2.f, 3.f, 4);
auto msg2 = msg.extract({ // remove float and integer pairs
[](float, float) { }, auto msg2 = msg.extract({
[](int, int) { } [](float, float) { },
}); [](int, int) { }
assert(msg2 == make_message(1, 4)); });
\end{lstlisting} assert(msg2 == make_message(1, 4));
Step-by-step explanation: Step-by-step explanation:
\begin{itemize} * Slice 1: ``(1, 2.f, 3.f, 4)``, no match
\item Slice 1: \lstinline^(1, 2.f, 3.f, 4)^, no match * Slice 2: ``(1, 2.f, 3.f)``, no match
\item Slice 2: \lstinline^(1, 2.f, 3.f)^, no match * Slice 3: ``(1, 2.f)``, no match
\item Slice 3: \lstinline^(1, 2.f)^, no match * Slice 4: ``(1)``, no match
\item Slice 4: \lstinline^(1)^, no match * Slice 5: ``(2.f, 3.f, 4)``, no match
\item Slice 5: \lstinline^(2.f, 3.f, 4)^, no match * Slice 6: ``(2.f, 3.f)``, *match*; new message is ``(1, 4)``
\item Slice 6: \lstinline^(2.f, 3.f)^, \emph{match}; new message is \lstinline^(1, 4)^ * Slice 7: ``(4)``, no match
\item Slice 7: \lstinline^(4)^, no match
\end{itemize} Slice 7 is ``(4)``, i.e., does not contain the first element, because
Slice 7 is \lstinline^(4)^, i.e., does not contain the first element, because
the match on slice 6 occurred at index position 1. The function the match on slice 6 occurred at index position 1. The function
\lstinline^extract^ iterates a message only once, from left to right. The ``extract`` iterates a message only once, from left to right. The
returned message contains the remaining, i.e., unmatched, elements. returned message contains the remaining, i.e., unmatched, elements.
\clearpage .. _extract-opts:
\subsection{Extracting Command Line Options}
\label{extract-opts} Extracting Command Line Options
-------------------------------
The class \lstinline^message^ also contains a convenience interface to
\lstinline^extract^ for parsing command line options: the member function The class ``message`` also contains a convenience interface to
\lstinline^extract_opts^. ``extract`` for parsing command line options: the member function
``extract_opts``.
\begin{lstlisting}
int main(int argc, char** argv) { .. code-block:: C++
uint16_t port;
string host = "localhost"; int main(int argc, char** argv) {
auto res = message_builder(argv + 1, argv + argc).extract_opts({ uint16_t port;
{"port,p", "set port", port}, string host = "localhost";
{"host,H", "set host (default: localhost)", host}, auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"verbose,v", "enable verbose mode"} {"port,p", "set port", port},
}); {"host,H", "set host (default: localhost)", host},
if (! res.error.empty()) { {"verbose,v", "enable verbose mode"}
// read invalid CLI arguments });
cerr << res.error << endl; if (! res.error.empty()) {
return 1; // read invalid CLI arguments
} cerr << res.error << endl;
if (res.opts.count("help") > 0) { return 1;
// CLI arguments contained "-h", "--help", or "-?" (builtin); }
cout << res.helptext << endl; if (res.opts.count("help") > 0) {
return 0; // CLI arguments contained "-h", "--help", or "-?" (builtin);
} cout << res.helptext << endl;
if (! res.remainder.empty()) { return 0;
// res.remainder stors all extra arguments that weren't consumed }
} if (! res.remainder.empty()) {
if (res.opts.count("verbose") > 0) { // res.remainder stors all extra arguments that weren't consumed
// enable verbose mode }
} if (res.opts.count("verbose") > 0) {
// ... // enable verbose mode
} }
// ...
/* }
Output of ./program_name -h:
/*
Allowed options: Output of ./program_name -h:
-p [--port] arg : set port
-H [--host] arg : set host (default: localhost) Allowed options:
-v [--verbose] : enable verbose mode -p [--port] arg : set port
*/ -H [--host] arg : set host (default: localhost)
\end{lstlisting} -v [--verbose] : enable verbose mode
*/
\section{Migration Guides} Migration Guides
================
The guides in this section document all possibly breaking changes in the The guides in this section document all possibly breaking changes in the
library for that last versions of CAF. library for that last versions of CAF.
\subsection{0.8 to 0.9} 0.8 to 0.9
----------
Version 0.9 included a lot of changes and improvements in its implementation, Version 0.9 included a lot of changes and improvements in its implementation,
but it also made breaking changes to the API. but it also made breaking changes to the API.
\paragraph{\lstinline^self^ has been removed} ``self`` has been removed
+++++++++++++++++++++++++
~
This is the biggest library change since the initial release. The major problem This is the biggest library change since the initial release. The major problem
with this keyword-like identifier is that it must have a single type as it's with this keyword-like identifier is that it must have a single type as it's
implemented as a thread-local variable. Since there are so many different kinds implemented as a thread-local variable. Since there are so many different kinds
of actors (event-based or blocking, untyped or typed), \lstinline^self^ needs of actors (event-based or blocking, untyped or typed), ``self`` needs
to perform type erasure at some point, rendering it ultimately useless. Instead to perform type erasure at some point, rendering it ultimately useless. Instead
of a thread-local pointer, you can now use the first argument in functor-based of a thread-local pointer, you can now use the first argument in functor-based
actors to "catch" the self pointer with proper type information. actors to "catch" the self pointer with proper type information.
\paragraph{\lstinline^actor_ptr^ has been replaced} ``actor_ptr`` has been replaced
+++++++++++++++++++++++++++++++
~
CAF now distinguishes between handles to actors, i.e., CAF now distinguishes between handles to actors, i.e.,
\lstinline^typed_actor<...>^ or simply \lstinline^actor^, and \emph{addresses} ``typed_actor<...>`` or simply ``actor``, and *addresses*
of actors, i.e., \lstinline^actor_addr^. The reason for this change is that of actors, i.e., ``actor_addr``. The reason for this change is that
each actor has a logical, (network-wide) unique address, which is used by the each actor has a logical, (network-wide) unique address, which is used by the
networking layer of CAF. Furthermore, for monitoring or linking, the address networking layer of CAF. Furthermore, for monitoring or linking, the address
is all you need. However, the address is not sufficient for sending messages, is all you need. However, the address is not sufficient for sending messages,
because it doesn't have any type information. The function because it doesn't have any type information. The function
\lstinline^current_sender()^ now returns the \emph{address} of the sender. This ``current_sender()`` now returns the *address* of the sender. This
means that previously valid code such as \lstinline^send(current_sender(),...)^ means that previously valid code such as ``send(current_sender(),...)``
will cause a compiler error. However, the recommended way of replying to will cause a compiler error. However, the recommended way of replying to
messages is to return the result from the message handler. messages is to return the result from the message handler.
\paragraph{The API for typed actors is now similar to the API for untyped actors} The API for typed actors is now similar to the API for untyped actors
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
~
The APIs of typed and untyped actors have been harmonized. Typed actors can now The APIs of typed and untyped actors have been harmonized. Typed actors can now
be published in the network and also use all operations untyped actors can. be published in the network and also use all operations untyped actors can.
\clearpage 0.9 to 0.10 (``libcppa`` to CAF)
\subsection{0.9 to 0.10 (\texttt{libcppa} to CAF)} --------------------------------
The first release under the new name CAF is an overhaul of the entire library. The first release under the new name CAF is an overhaul of the entire library.
Some classes have been renamed or relocated, others have been removed. The Some classes have been renamed or relocated, others have been removed. The
purpose of this refactoring was to make the library easier to grasp and to make purpose of this refactoring was to make the library easier to grasp and to make
its API more consistent. All classes now live in the namespace \texttt{caf} and its API more consistent. All classes now live in the namespace ``caf`` and
all headers have the top level folder \texttt{caf} instead of \texttt{cppa}. all headers have the top level folder ``caf`` instead of ``cppa``.
For example, \texttt{cppa/actor.hpp} becomes \texttt{caf/actor.hpp}. Further, For example, ``cppa/actor.hpp`` becomes ``caf/actor.hpp``. Further,
the convenience header to get all parts of the user API is now the convenience header to get all parts of the user API is now
\texttt{"caf/all.hpp"}. The networking has been separated from the core ``"caf/all.hpp"``. The networking has been separated from the core
library. To get the networking components, simply include library. To get the networking components, simply include
\texttt{caf/io/all.hpp} and use the namespace \lstinline^caf::io^. ``caf/io/all.hpp`` and use the namespace ``caf::io``.
Version 0.10 still includes the header \texttt{cppa/cppa.hpp} to make the Version 0.10 still includes the header ``cppa/cppa.hpp`` to make the
transition process for users easier and to not break existing code right away. transition process for users easier and to not break existing code right away.
The header defines the namespace \texttt{cppa} as an alias for \texttt{caf}. The header defines the namespace ``cppa`` as an alias for ``caf``.
Furthermore, it provides implementations or type aliases for renamed or removed Furthermore, it provides implementations or type aliases for renamed or removed
classes such as \lstinline^cow_tuple^. You won't get any warning about deprecated classes such as ``cow_tuple``. You won't get any warning about deprecated
headers with 0.10. However, we will add this warnings in the next library headers with 0.10. However, we will add this warnings in the next library
version and remove deprecated code eventually. version and remove deprecated code eventually.
...@@ -73,105 +72,102 @@ the library but a whole lot of code that is hard to maintain in the long run ...@@ -73,105 +72,102 @@ the library but a whole lot of code that is hard to maintain in the long run
due to its complexity. Using projections to not only perform type conversions due to its complexity. Using projections to not only perform type conversions
but also to restrict values is the more natural choice. but also to restrict values is the more natural choice.
\lstinline^any_tuple => message^ ``any_tuple => message``
This type is only being used to pass a message from one actor to another. This type is only being used to pass a message from one actor to another.
Hence, \lstinline^message^ is the logical name. Hence, ``message`` is the logical name.
\lstinline^partial_function => message_handler^ ``partial_function => message_handler``
Technically, it still is a partial function. However, we wanted to put Technically, it still is a partial function. However, we wanted to put
emphasize on its use case. emphasize on its use case.
\lstinline^cow_tuple => X^ ``cow_tuple => X``
We want to provide a streamlined, simple API. Shipping a full tuple abstraction We want to provide a streamlined, simple API. Shipping a full tuple abstraction
with the library does not fit into this philosophy. The removal of with the library does not fit into this philosophy. The removal of
\lstinline^cow_tuple^ implies the removal of related functions such as ``cow_tuple`` implies the removal of related functions such as
\lstinline^tuple_cast^. ``tuple_cast``.
\lstinline^cow_ptr => X^ ``cow_ptr => X``
This pointer class is an implementation detail of \lstinline^message^ and This pointer class is an implementation detail of ``message`` and
should not live in the global namespace in the first place. It also had the should not live in the global namespace in the first place. It also had the
wrong name, because it is intrusive. wrong name, because it is intrusive.
\lstinline^X => message_builder^ ``X => message_builder``
This new class can be used to create messages dynamically. For example, the This new class can be used to create messages dynamically. For example, the
content of a vector can be used to create a message using a series of content of a vector can be used to create a message using a series of
\lstinline^append^ calls. ``append`` calls.
.. code-block:: C++
\begin{lstlisting} accept_handle, connection_handle, publish, remote_actor,
accept_handle, connection_handle, publish, remote_actor, max_msg_size, typed_publish, typed_remote_actor, publish_local_groups,
max_msg_size, typed_publish, typed_remote_actor, publish_local_groups, new_connection_msg, new_data_msg, connection_closed_msg, acceptor_closed_msg
new_connection_msg, new_data_msg, connection_closed_msg, acceptor_closed_msg
\end{lstlisting}
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
\lstinline^announce^, i.e., \lstinline^announce<my_class>(...)^ becomes
\lstinline^announce<my_class>("my_class", ...)^.
\item Synchronous send blocks no longer support \lstinline^continue_with^. This
feature has been removed without substitution.
\end{itemize}
\subsection{0.12 to 0.13} 0.12 to 0.13
------------
This release removes the (since 0.9 deprecated) \lstinline^cppa^ headers and This release removes the (since 0.9 deprecated) ``cppa`` headers and
deprecates all \lstinline^*_send_tuple^ versions (simply use the function deprecates all ``*_send_tuple`` versions (simply use the function
without \lstinline^_tuple^ suffix). \lstinline^local_actor::on_exit^ once again without ``_tuple`` suffix). ``local_actor::on_exit`` once again
became virtual. became virtual.
In case you were using the old \lstinline^cppa::options_description^ API, you In case you were using the old ``cppa::options_description`` API, you can
can migrate to the new API based on \lstinline^extract^ \see{extract-opts}. migrate to the new API based on ``extract`` (see :ref:`extract-opts`).
Most importantly, version 0.13 slightly changes \lstinline^last_dequeued^ and Most importantly, version 0.13 slightly changes ``last_dequeued`` and
\lstinline^last_sender^. Both functions will now cause undefined behavior ``last_sender``. Both functions will now cause undefined behavior (dereferencing
(dereferencing a \lstinline^nullptr^) instead of returning dummy values when a ``nullptr``) instead of returning dummy values when accessed from outside a
accessed from outside a callback or after forwarding the current message. callback or after forwarding the current message. Besides, these function names
Besides, these function names were not a good choice in the first place, since were not a good choice in the first place, since "last" implies accessing data
``last'' implies accessing data received in the past. As a result, both received in the past. As a result, both functions are now deprecated. Their
functions are now deprecated. Their replacements are named replacements are named ``current_message`` and ``current_sender`` (see
\lstinline^current_message^ and \lstinline^current_sender^ \see{interface}. :ref:`interface`).
\subsection{0.13 to 0.14} 0.13 to 0.14
------------
The function \lstinline^timed_sync_send^ has been removed. It offered an The function ``timed_sync_send`` has been removed. It offered an
alternative way of defining message handlers, which is inconsistent with the alternative way of defining message handlers, which is inconsistent with the
rest of the API. rest of the API.
The policy classes \lstinline^broadcast^, \lstinline^random^, and The policy classes ``broadcast``, ``random``, and
\lstinline^round_robin^ in \lstinline^actor_pool^ were removed and replaced by ``round_robin`` in ``actor_pool`` were removed and replaced by
factory functions using the same name. factory functions using the same name.
\clearpage 0.14 to 0.15
\subsection{0.14 to 0.15} ------------
Version 0.15 replaces the singleton-based architecture with Version 0.15 replaces the singleton-based architecture with ``actor_system``.
\lstinline^actor_system^. Most of the free functions in namespace Most of the free functions in namespace ``caf`` are now member functions of
\lstinline^caf^ are now member functions of \lstinline^actor_system^ ``actor_system`` (see :ref:`actor-system`). Likewise, most functions in
\see{actor-system}. Likewise, most functions in namespace \lstinline^caf::io^ namespace ``caf::io`` are now member functions of ``middleman`` (see
are now member functions of \lstinline^middleman^ \see{middleman}. The :ref:`middleman`). The structure of CAF applications has changed fundamentally
structure of CAF applications has changed fundamentally with a focus on with a focus on configurability. Setting and fine-tuning the scheduler, changing
configurability. Setting and fine-tuning the scheduler, changing parameters of parameters of the middleman, etc. is now bundled in the class
the middleman, etc. is now bundled in the class ``actor_system_config``. The new configuration mechanism is also easily
\lstinline^actor_system_config^. The new configuration mechanism is also easily
extensible. extensible.
Patterns are now limited to the simple notation, because the advanced features Patterns are now limited to the simple notation, because the advanced features
...@@ -179,5 +175,5 @@ Patterns are now limited to the simple notation, because the advanced features ...@@ -179,5 +175,5 @@ Patterns are now limited to the simple notation, because the advanced features
Windows/MSVC, and (3) drastically impact compile times. Dropping this Windows/MSVC, and (3) drastically impact compile times. Dropping this
functionality also simplifies the implementation and improves performance. functionality also simplifies the implementation and improves performance.
The \lstinline^blocking_api^ flag has been removed. All variants of The ``blocking_api`` flag has been removed. All variants of
\emph{spawn} now auto-detect blocking actors. *spawn* now auto-detect blocking actors.
\section{Middleman} .. _middleman:
\label{middleman}
Middleman
=========
The middleman is the main component of the I/O module and enables distribution. The middleman is the main component of the I/O module and enables distribution.
It transparently manages proxy actor instances representing remote actors, It transparently manages proxy actor instances representing remote actors,
maintains connections to other nodes, and takes care of serialization of maintains connections to other nodes, and takes care of serialization of
messages. Applications install a middleman by loading messages. Applications install a middleman by loading ``caf::io::middleman`` as
\lstinline^caf::io::middleman^ as module~\see{system-config}. Users can include module (see :ref:`system-config`). Users can include ``"caf/io/all.hpp"`` to get
\lstinline^"caf/io/all.hpp"^ to get access to all public classes of the I/O access to all public classes of the I/O module.
module.
Class ``middleman``
\subsection{Class \texttt{middleman}} -------------------
\begin{center} +---------------------------------------------------------------+----------------------+
\begin{tabular}{ll} | **Remoting** | |
\textbf{Remoting} & ~ \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<uint16> open(uint16, const char*, bool)`` | See :ref:`remoting`. |
\lstinline^expected<uint16> open(uint16, const char*, bool)^ & See~\sref{remoting}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<uint16> publish(T, uint16, const char*, bool)`` | See :ref:`remoting`. |
\lstinline^expected<uint16> publish(T, uint16, const char*, bool)^ & See~\sref{remoting}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<void> unpublish(T x, uint16)`` | See :ref:`remoting`. |
\lstinline^expected<void> unpublish(T x, uint16)^ & See~\sref{remoting}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<node_id> connect(std::string host, uint16_t port)``| See :ref:`remoting`. |
\lstinline^expected<node_id> connect(std::string host, uint16_t port)^ & See~\sref{remoting}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<T> remote_actor<T = actor>(string, uint16)`` | See :ref:`remoting`. |
\lstinline^expected<T> remote_actor<T = actor>(string, uint16)^ & See~\sref{remoting}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<T> spawn_broker(F fun, ...)`` | See :ref:`broker`. |
\lstinline^expected<T> spawn_broker(F fun, ...)^ & See~\sref{broker}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<T> spawn_client(F, string, uint16, ...)`` | See :ref:`broker`. |
\lstinline^expected<T> spawn_client(F, string, uint16, ...)^ & See~\sref{broker}. \\ +---------------------------------------------------------------+----------------------+
\hline | ``expected<T> spawn_server(F, uint16, ...)`` | See :ref:`broker`. |
\lstinline^expected<T> spawn_server(F, uint16, ...)^ & See~\sref{broker}. \\ +---------------------------------------------------------------+----------------------+
\hline
\end{tabular} .. _remoting:
\end{center}
Publishing and Connecting
\subsection{Publishing and Connecting} -------------------------
\label{remoting}
The member function ``publish`` binds an actor to a given port, thereby
The member function \lstinline^publish^ binds an actor to a given port, thereby
allowing other nodes to access it over the network. allowing other nodes to access it over the network.
\begin{lstlisting} .. code-block:: C++
template <class T>
expected<uint16_t> middleman::publish(T x, uint16_t port, template <class T>
const char* in = nullptr, expected<uint16_t> middleman::publish(T x, uint16_t port,
bool reuse_addr = false); const char* in = nullptr,
\end{lstlisting} bool reuse_addr = false);
The first argument is a handle of type \lstinline^actor^ or The first argument is a handle of type ``actor`` or
\lstinline^typed_actor<...>^. The second argument denotes the TCP port. The OS ``typed_actor<...>``. The second argument denotes the TCP port. The OS
will pick a random high-level port when passing 0. The third parameter will pick a random high-level port when passing 0. The third parameter
configures the listening address. Passing null will accept all incoming configures the listening address. Passing null will accept all incoming
connections (\lstinline^INADDR_ANY^). Finally, the flag \lstinline^reuse_addr^ connections (``INADDR_ANY``). Finally, the flag ``reuse_addr``
controls the behavior when binding an IP address to a port, with the same controls the behavior when binding an IP address to a port, with the same
semantics as the BSD socket flag \lstinline^SO_REUSEADDR^. For example, with semantics as the BSD socket flag ``SO_REUSEADDR``. For example, with
\lstinline^reuse_addr = false^, binding two sockets to 0.0.0.0:42 and ``reuse_addr = false``, binding two sockets to 0.0.0.0:42 and
10.0.0.1:42 will fail with \texttt{EADDRINUSE} since 0.0.0.0 includes 10.0.0.1. 10.0.0.1:42 will fail with ``EADDRINUSE`` since 0.0.0.0 includes 10.0.0.1.
With \lstinline^reuse_addr = true^ binding would succeed because 10.0.0.1 and With ``reuse_addr = true`` binding would succeed because 10.0.0.1 and
0.0.0.0 are not literally equal addresses. 0.0.0.0 are not literally equal addresses.
The member function returns the bound port on success. Otherwise, an The member function returns the bound port on success. Otherwise, an ``error``
\lstinline^error^ \see{error} is returned. (see :ref:`error`) is returned.
.. code-block:: C++
\begin{lstlisting} template <class T>
template <class T> expected<uint16_t> middleman::unpublish(T x, uint16_t port = 0);
expected<uint16_t> middleman::unpublish(T x, uint16_t port = 0);
\end{lstlisting}
The member function \lstinline^unpublish^ allows actors to close a port The member function ``unpublish`` allows actors to close a port
manually. This is performed automatically if the published actor terminates. manually. This is performed automatically if the published actor terminates.
Passing 0 as second argument closes all ports an actor is published to, Passing 0 as second argument closes all ports an actor is published to,
otherwise only one specific port is closed. otherwise only one specific port is closed.
The function returns an \lstinline^error^ \see{error} if the actor was not The function returns an ``error`` (see :ref:`error`) if the actor was not bound
bound to given port. to given port.
\clearpage .. code-block:: C++
\begin{lstlisting}
template<class T = actor> template<class T = actor>
expected<T> middleman::remote_actor(std::string host, uint16_t port); expected<T> middleman::remote_actor(std::string host, uint16_t port);
\end{lstlisting}
After a server has published an actor with ``publish``, clients can
After a server has published an actor with \lstinline^publish^, clients can connect to the published actor by calling ``remote_actor``:
connect to the published actor by calling \lstinline^remote_actor^:
.. code-block:: C++
\begin{lstlisting}
// node A // node A
auto ping = spawn(ping); auto ping = spawn(ping);
system.middleman().publish(ping, 4242); system.middleman().publish(ping, 4242);
// node B // node B
auto ping = system.middleman().remote_actor("node A", 4242); auto ping = system.middleman().remote_actor("node A", 4242);
if (! ping) { if (! ping) {
cerr << "unable to connect to node A: " cerr << "unable to connect to node A: "
<< system.render(ping.error()) << std::endl; << system.render(ping.error()) << std::endl;
} else { } else {
self->send(*ping, ping_atom::value); self->send(*ping, ping_atom::value);
} }
\end{lstlisting}
There is no difference between server and client after the connection phase. There is no difference between server and client after the connection phase.
Remote actors use the same handle types as local actors and are thus fully Remote actors use the same handle types as local actors and are thus fully
transparent. transparent.
The function pair \lstinline^open^ and \lstinline^connect^ allows users to The function pair ``open`` and ``connect`` allows users to connect CAF instances
connect CAF instances without remote actor setup. The function without remote actor setup. The function ``connect`` returns a ``node_id`` that
\lstinline^connect^ returns a \lstinline^node_id^ that can be used for remote can be used for remote spawning (see (see :ref:`remote-spawn`)).
spawning (see~\sref{remote-spawn}).
\subsection{Free Functions} .. _free-remoting-functions:
\label{free-remoting-functions}
The following free functions in the namespace \lstinline^caf::io^ avoid calling Free Functions
--------------
The following free functions in the namespace ``caf::io`` avoid calling
the middleman directly. This enables users to easily switch between the middleman directly. This enables users to easily switch between
communication backends as long as the interfaces have the same signatures. For communication backends as long as the interfaces have the same signatures. For
example, the (experimental) OpenSSL binding of CAF implements the same example, the (experimental) OpenSSL binding of CAF implements the same
functions in the namespace \lstinline^caf::openssl^ to easily switch between functions in the namespace ``caf::openssl`` to easily switch between
encrypted and unencrypted communication. encrypted and unencrypted communication.
\begin{center} +------------------------------------------------------------------------------+----------------------+
\begin{tabular}{ll} | ``expected<uint16> open(actor_system&, uint16, const char*, bool)`` | See :ref:`remoting`. |
\hline +------------------------------------------------------------------------------+----------------------+
\lstinline^expected<uint16> open(actor_system&, uint16, const char*, bool)^ & See~\sref{remoting}. \\ | ``expected<uint16> publish(T, uint16, const char*, bool)`` | See :ref:`remoting`. |
\hline +------------------------------------------------------------------------------+----------------------+
\lstinline^expected<uint16> publish(T, uint16, const char*, bool)^ & See~\sref{remoting}. \\ | ``expected<void> unpublish(T x, uint16)`` | See :ref:`remoting`. |
\hline +------------------------------------------------------------------------------+----------------------+
\lstinline^expected<void> unpublish(T x, uint16)^ & See~\sref{remoting}. \\ | ``expected<node_id> connect(actor_system&, std::string host, uint16_t port)``| See :ref:`remoting`. |
\hline +------------------------------------------------------------------------------+----------------------+
\lstinline^expected<node_id> connect(actor_system&, std::string host, uint16_t port)^ & See~\sref{remoting}. \\ | ``expected<T> remote_actor<T = actor>(actor_system&, string, uint16)`` | See :ref:`remoting`. |
\hline +------------------------------------------------------------------------------+----------------------+
\lstinline^expected<T> remote_actor<T = actor>(actor_system&, string, uint16)^ & See~\sref{remoting}. \\
\hline .. _transport-protocols:
\end{tabular}
\end{center} Transport Protocols :sup:`experimental`
----------------------------------------
\subsection{Transport Protocols \experimental}
\label{transport-protocols}
CAF communication uses TCP per default and thus the functions shown in the CAF communication uses TCP per default and thus the functions shown in the
middleman API above are related to TCP. There are two alternatives to plain middleman API above are related to TCP. There are two alternatives to plain TCP:
TCP: TLS via the OpenSSL module shortly discussed in TLS via the OpenSSL module shortly discussed in (see
\sref{free-remoting-functions} and UDP. :ref:`free-remoting-functions`) and UDP.
UDP is integrated in the default multiplexer and BASP broker. Set the flag UDP is integrated in the default multiplexer and BASP broker. Set the flag
\lstinline^middleman_enable_udp^ to true to enable it ``middleman_enable_udp`` to true to enable it (see :ref:`system-config`). This
(see~\sref{system-config}). This does not require you to disable TCP. Use does not require you to disable TCP. Use ``publish_udp`` and
\lstinline^publish_udp^ and \lstinline^remote_actor_udp^ to establish ``remote_actor_udp`` to establish communication.
communication.
Communication via UDP is inherently unreliable and unordered. CAF reestablishes Communication via UDP is inherently unreliable and unordered. CAF reestablishes
order and drops messages that arrive late. Messages that are sent via datagrams order and drops messages that arrive late. Messages that are sent via datagrams
......
\section{Overview} Overview
========
Compiling CAF requires CMake and a C++11-compatible compiler. To get and Compiling CAF requires CMake and a C++11-compatible compiler. To get and
compile the sources on UNIX-like systems, type the following in a terminal: compile the sources on UNIX-like systems, type the following in a terminal:
\begin{verbatim} .. ::
git clone https://github.com/actor-framework/actor-framework
cd actor-framework git clone https://github.com/actor-framework/actor-framework
./configure cd actor-framework
make ./configure
make install [as root, optional] make
\end{verbatim} make install [as root, optional]
We recommended to run the unit tests as well: We recommended to run the unit tests as well:
\begin{verbatim} .. ::
make test
\end{verbatim} make test
If the output indicates an error, please submit a bug report that includes (a) If the output indicates an error, please submit a bug report that includes (a)
your compiler version, (b) your OS, and (c) the content of the file your compiler version, (b) your OS, and (c) the content of the file
\texttt{build/Testing/Temporary/LastTest.log}. ``build/Testing/Temporary/LastTest.log``.
\subsection{Features} Features
--------
\begin{itemize} * Lightweight, fast and efficient actor implementations
\item Lightweight, fast and efficient actor implementations * Network transparent messaging
\item Network transparent messaging * Error handling based on Erlang's failure model
\item Error handling based on Erlang's failure model * Pattern matching for messages as internal DSL to ease development
\item Pattern matching for messages as internal DSL to ease development * Thread-mapped actors for soft migration of existing applications
\item Thread-mapped actors for soft migration of existing applications * Publish/subscribe group communication
\item Publish/subscribe group communication
\end{itemize}
Minimal Compiler Versions
-------------------------
\subsection{Minimal Compiler Versions} * GCC 4.8
* Clang 3.4
* Visual Studio 2015, Update 3
\begin{itemize} Supported Operating Systems
\item GCC 4.8 ---------------------------
\item Clang 3.4
\item Visual Studio 2015, Update 3
\end{itemize}
\subsection{Supported Operating Systems} * Linux
* Mac OS X
* Windows (static library only)
\begin{itemize} Hello World Example
\item Linux -------------------
\item Mac OS X
\item Windows (static library only)
\end{itemize}
\clearpage .. literalinclude:: /examples/hello_world.cpp
\subsection{Hello World Example} :language: C++
\cppexample{hello_world}
\section{Reference Counting} .. _reference-counting:
\label{reference-counting}
Reference Counting
==================
Actors systems can span complex communication graphs that make it hard to Actors systems can span complex communication graphs that make it hard to
decide when actors are no longer needed. As a result, manually managing decide when actors are no longer needed. As a result, manually managing
...@@ -7,13 +9,14 @@ lifetime of actors is merely impossible. For this reason, CAF implements a ...@@ -7,13 +9,14 @@ lifetime of actors is merely impossible. For this reason, CAF implements a
garbage collection strategy for actors based on weak and strong reference garbage collection strategy for actors based on weak and strong reference
counts. counts.
\subsection{Shared Ownership in C++} Shared Ownership in C++
-----------------------
The C++ standard library already offers \lstinline^shared_ptr^ and The C++ standard library already offers ``shared_ptr`` and
\lstinline^weak_ptr^ to manage objects with complex shared ownership. The ``weak_ptr`` to manage objects with complex shared ownership. The
standard implementation is a solid general purpose design that covers most use standard implementation is a solid general purpose design that covers most use
cases. Weak and strong references to an object are stored in a \emph{control cases. Weak and strong references to an object are stored in a *control
block}. However, CAF uses a slightly different design. The reason for this is block*. However, CAF uses a slightly different design. The reason for this is
twofold. First, we need the control block to store the identity of an actor. twofold. First, we need the control block to store the identity of an actor.
Second, we wanted a design that requires less indirections, because actor Second, we wanted a design that requires less indirections, because actor
handles are used extensively copied for messaging, and this overhead adds up. handles are used extensively copied for messaging, and this overhead adds up.
...@@ -21,126 +24,129 @@ handles are used extensively copied for messaging, and this overhead adds up. ...@@ -21,126 +24,129 @@ handles are used extensively copied for messaging, and this overhead adds up.
Before discussing the approach to shared ownership in CAF, we look at the Before discussing the approach to shared ownership in CAF, we look at the
design of shared pointers in the C++ standard library. design of shared pointers in the C++ standard library.
\singlefig{shared_ptr}{Shared pointer design in the C++ standard library}{shared-ptr} .. _shared-ptr:
.. image:: shared_ptr.png
:alt: Shared pointer design in the C++ standard library
The figure above depicts the default memory layout when using shared pointers. The figure above depicts the default memory layout when using shared pointers.
The control block is allocated separately from the data and thus stores a The control block is allocated separately from the data and thus stores a
pointer to the data. This is when using manually-allocated objects, for example pointer to the data. This is when using manually-allocated objects, for example
\lstinline^shared_ptr<int> iptr{new int}^. The benefit of this design is that ``shared_ptr<int> iptr{new int}``. The benefit of this design is that
one can destroy \lstinline^T^ independently from its control block. While one can destroy ``T`` independently from its control block. While
irrelevant for small objects, it can become an issue for large objects. irrelevant for small objects, it can become an issue for large objects.
Notably, the shared pointer stores two pointers internally. Otherwise, Notably, the shared pointer stores two pointers internally. Otherwise,
dereferencing it would require to get the data location from the control block dereferencing it would require to get the data location from the control block
first. first.
\singlefig{make_shared}{Memory layout when using \lstinline^std::make_shared^}{make-shared} .. _make-shared:
.. image:: make_shared.png
:alt: Memory layout when using ``std::make_shared``
When using \lstinline^make_shared^ or \lstinline^allocate_shared^, the standard When using ``make_shared`` or ``allocate_shared``, the standard
library can store reference count and data in a single memory block as shown library can store reference count and data in a single memory block as shown
above. However, \lstinline^shared_ptr^ still has to store two pointers, because above. However, ``shared_ptr`` still has to store two pointers, because
it is unaware where the data is allocated. it is unaware where the data is allocated.
\singlefig{enable_shared_from_this}{Memory layout with \lstinline^std::enable_shared_from_this^}{enable-shared-from-this} .. _enable-shared-from-this:
.. image:: enable_shared_from_this.png
:alt: Memory layout with ``std::enable_shared_from_this``
Finally, the design of the standard library becomes convoluted when an object Finally, the design of the standard library becomes convoluted when an object
should be able to hand out a \lstinline^shared_ptr^ to itself. Classes must should be able to hand out a ``shared_ptr`` to itself. Classes must
inherit from \lstinline^std::enable_shared_from_this^ to navigate from an inherit from ``std::enable_shared_from_this`` to navigate from an
object to its control block. This additional navigation path is required, object to its control block. This additional navigation path is required,
because \lstinline^std::shared_ptr^ needs two pointers. One to the data and one because ``std::shared_ptr`` needs two pointers. One to the data and one
to the control block. Programmers can still use \lstinline^make_shared^ for to the control block. Programmers can still use ``make_shared`` for
such objects, in which case the object is again stored along with the control such objects, in which case the object is again stored along with the control
block. block.
\subsection{Smart Pointers to Actors} Smart Pointers to Actors
------------------------
In CAF, we use a different approach than the standard library because (1) we In CAF, we use a different approach than the standard library because (1) we
always allocate actors along with their control block, (2) we need additional always allocate actors along with their control block, (2) we need additional
information in the control block, and (3) we can store only a single raw information in the control block, and (3) we can store only a single raw
pointer internally instead of the two raw pointers \lstinline^std::shared_ptr^ pointer internally instead of the two raw pointers ``std::shared_ptr``
needs. The following figure summarizes the design of smart pointers to actors. needs. The following figure summarizes the design of smart pointers to actors.
\singlefig{refcounting}{Shared pointer design in CAF}{actor-pointer} .. image:: refcounting.png
:alt: Shared pointer design in CAF
CAF uses \lstinline^strong_actor_ptr^ instead of CAF uses ``strong_actor_ptr`` instead of
\lstinline^std::shared_ptr<...>^ and \lstinline^weak_actor_ptr^ instead of ``std::shared_ptr<...>`` and ``weak_actor_ptr`` instead of
\lstinline^std::weak_ptr<...>^. Unlike the counterparts from the standard ``std::weak_ptr<...>``. Unlike the counterparts from the standard
library, both smart pointer types only store a single pointer. library, both smart pointer types only store a single pointer.
Also, the control block in CAF is not a template and stores the identity of an Also, the control block in CAF is not a template and stores the identity of an
actor (\lstinline^actor_id^ plus \lstinline^node_id^). This allows CAF to actor (``actor_id`` plus ``node_id``). This allows CAF to
access this information even after an actor died. The control block fits access this information even after an actor died. The control block fits
exactly into a single cache line (64 Bytes). This makes sure no \emph{false exactly into a single cache line (64 Bytes). This makes sure no *false
sharing} occurs between an actor and other actors that have references to it. sharing* occurs between an actor and other actors that have references to it.
Since the size of the control block is fixed and CAF \emph{guarantees} the Since the size of the control block is fixed and CAF *guarantees* the
memory layout enforced by \lstinline^actor_storage^, CAF can compute the memory layout enforced by ``actor_storage``, CAF can compute the
address of an actor from the pointer to its control block by offsetting it by address of an actor from the pointer to its control block by offsetting it by
64 Bytes. Likewise, an actor can compute the address of its control block. 64 Bytes. Likewise, an actor can compute the address of its control block.
The smart pointer design in CAF relies on a few assumptions about actor types. The smart pointer design in CAF relies on a few assumptions about actor types.
Most notably, the actor object is placed 64 Bytes after the control block. This Most notably, the actor object is placed 64 Bytes after the control block. This
starting address is cast to \lstinline^abstract_actor*^. Hence, \lstinline^T*^ starting address is cast to ``abstract_actor*``. Hence, ``T*``
must be convertible to \lstinline^abstract_actor*^ via must be convertible to ``abstract_actor*`` via
\lstinline^reinterpret_cast^. In practice, this means actor subclasses must not ``reinterpret_cast``. In practice, this means actor subclasses must not
use virtual inheritance, which is enforced in CAF with a use virtual inheritance, which is enforced in CAF with a
\lstinline^static_assert^. ``static_assert``.
\subsection{Strong and Weak References} Strong and Weak References
--------------------------
A \emph{strong} reference manipulates the \lstinline^strong refs^ counter as
shown above. An actor is destroyed if there are \emph{zero} strong references A *strong* reference manipulates the ``strong refs`` counter as shown above. An
to it. If two actors keep strong references to each other via member variable, actor is destroyed if there are *zero* strong references to it. If two actors
neither actor can ever be destroyed because they produce a cycle keep strong references to each other via member variable, neither actor can ever
\see{breaking-cycles}. Strong references are formed by be destroyed because they produce a cycle (see :ref:`breaking-cycles`). Strong
\lstinline^strong_actor_ptr^, \lstinline^actor^, and references are formed by ``strong_actor_ptr``, ``actor``, and
\lstinline^typed_actor<...>^ \see{actor-reference}. ``typed_actor<...>`` (see :ref:`actor-reference`).
A \emph{weak} reference manipulates the \lstinline^weak refs^ counter. This A *weak* reference manipulates the ``weak refs`` counter. This counter keeps
counter keeps track of how many references to the control block exist. The track of how many references to the control block exist. The control block is
control block is destroyed if there are \emph{zero} weak references to an actor destroyed if there are *zero* weak references to an actor (which cannot occur
(which cannot occur before \lstinline^strong refs^ reached \emph{zero} as before ``strong refs`` reached *zero* as well). No cycle occurs if two actors
well). No cycle occurs if two actors keep weak references to each other, keep weak references to each other, because the actor objects themselves can get
because the actor objects themselves can get destroyed independently from their destroyed independently from their control block. A weak reference is only
control block. A weak reference is only formed by \lstinline^actor_addr^ formed by ``actor_addr`` (see :ref:`actor-address`).
\see{actor-address}.
.. _actor-cast:
\subsection{Converting Actor References with \lstinline^actor_cast^}
\label{actor-cast} Converting Actor References with ``actor_cast``
-----------------------------------------------
The function \lstinline^actor_cast^ converts between actor pointers and
handles. The first common use case is to convert a \lstinline^strong_actor_ptr^ The function ``actor_cast`` converts between actor pointers and
to either \lstinline^actor^ or \lstinline^typed_actor<...>^ before being able handles. The first common use case is to convert a ``strong_actor_ptr``
to either ``actor`` or ``typed_actor<...>`` before being able
to send messages to an actor. The second common use case is to convert to send messages to an actor. The second common use case is to convert
\lstinline^actor_addr^ to \lstinline^strong_actor_ptr^ to upgrade a weak ``actor_addr`` to ``strong_actor_ptr`` to upgrade a weak
reference to a strong reference. Note that casting \lstinline^actor_addr^ to a reference to a strong reference. Note that casting ``actor_addr`` to a
strong actor pointer or handle can result in invalid handles. The syntax for strong actor pointer or handle can result in invalid handles. The syntax for
\lstinline^actor_cast^ resembles builtin C++ casts. For example, ``actor_cast`` resembles builtin C++ casts. For example,
\lstinline^actor_cast<actor>(x)^ converts \lstinline^x^ to an handle of type ``actor_cast<actor>(x)`` converts ``x`` to an handle of type
\lstinline^actor^. ``actor``.
.. _breaking-cycles:
\subsection{Breaking Cycles Manually} Breaking Cycles Manually
\label{breaking-cycles} ------------------------
Cycles can occur only when using class-based actors when storing references to Cycles can occur only when using class-based actors when storing references to
other actors via member variable. Stateful actors \see{stateful-actor} break other actors via member variable. Stateful actors (see :ref:`stateful-actor`)
cycles by destroying the state when an actor terminates, \emph{before} the break cycles by destroying the state when an actor terminates, *before* the
destructor of the actor itself runs. This means an actor releases all destructor of the actor itself runs. This means an actor releases all references
references to others automatically after calling \lstinline^quit^. However, to others automatically after calling ``quit``. However, class-based actors have
class-based actors have to break cycles manually, because references to others to break cycles manually, because references to others are not released until
are not released until the destructor of an actor runs. Two actors storing the destructor of an actor runs. Two actors storing references to each other via
references to each other via member variable produce a cycle and neither member variable produce a cycle and neither destructor can ever be called.
destructor can ever be called.
Class-based actors can break cycles manually by overriding ``on_exit()`` and
Class-based actors can break cycles manually by overriding calling ``destroy(x)`` on each handle (see :ref:`actor-handle`). Using a handle
\lstinline^on_exit()^ and calling \lstinline^destroy(x)^ on each after destroying it is undefined behavior, but it is safe to assign a new value
handle~\see{actor-handle}. Using a handle after destroying it is undefined to the handle.
behavior, but it is safe to assign a new value to the handle.
%TODO: Add use case for the following casting scenario. There is one
%requirement of this design: `static_cast<abstract_actor*>(self)` must return
%the same pointer as `reinterpret_cast<abstract_actor*>(self)` for any actor
%`self`. Otherwise, our assumption that the actor object starts exactly 64
%Bytes after its control block would break. Luckily, this boils down to a
%single limitation in practice: User-defined actors must not use virtual
%inheritance. When trying to spawn actors that do make use of virtual
%inheritance, CAF generates a compile-time error: `"actor subtype has illegal
%memory alignment (probably due to virtual inheritance)"`.
\section{Registry} .. _registry:
\label{registry}
The actor registry in CAF keeps track of the number of running actors and Registry
allows to map actors to their ID or a custom atom~\see{atom} representing a ========
name. The registry does \emph{not} contain all actors. Actors have to be stored
in the registry explicitly. Users can access the registry through an actor The actor registry in CAF keeps track of the number of running actors and allows
system by calling \lstinline^system.registry()^. The registry stores actors to map actors to their ID or a custom atom (see :ref:`atom`) representing a
using \lstinline^strong_actor_ptr^ \see{actor-pointer}. name. The registry does *not* contain all actors. Actors have to be stored in
the registry explicitly. Users can access the registry through an actor system
by calling ``system.registry()``. The registry stores actors using
``strong_actor_ptr`` (see :ref:`actor-pointer`).
Users can use the registry to make actors system-wide available by name. The Users can use the registry to make actors system-wide available by name. The
middleman~\see{middleman} uses the registry to keep track of all actors known :ref:`middleman` uses the registry to keep track of all actors known to remote
to remote nodes in order to serialize and deserialize them. Actors are removed nodes in order to serialize and deserialize them. Actors are removed
automatically when they terminate. automatically when they terminate.
It is worth mentioning that the registry is not synchronized between connected It is worth mentioning that the registry is not synchronized between connected
actor system. Each actor system has its own, local registry in a distributed actor system. Each actor system has its own, local registry in a distributed
setting. setting.
\begin{center} +-------------------------------------------+-------------------------------------------------+
\begin{tabular}{ll} | **Types** | |
\textbf{Types} & ~ \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``name_map`` | ``unordered_map<atom_value, strong_actor_ptr>`` |
\lstinline^name_map^ & \lstinline^unordered_map<atom_value, strong_actor_ptr>^ \\ +-------------------------------------------+-------------------------------------------------+
\hline | | |
~ & ~ \\ \textbf{Observers} & ~ \\ +-------------------------------------------+-------------------------------------------------+
\hline | **Observers** | |
\lstinline^strong_actor_ptr get(actor_id)^ & Returns the actor associated to given ID. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``strong_actor_ptr get(actor_id)`` | Returns the actor associated to given ID. |
\lstinline^strong_actor_ptr get(atom_value)^ & Returns the actor associated to given name. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``strong_actor_ptr get(atom_value)`` | Returns the actor associated to given name. |
\lstinline^name_map named_actors()^ & Returns all name mappings. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``name_map named_actors()`` | Returns all name mappings. |
\lstinline^size_t running()^ & Returns the number of currently running actors. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``size_t running()`` | Returns the number of currently running actors. |
~ & ~ \\ \textbf{Modifiers} & ~ \\ +-------------------------------------------+-------------------------------------------------+
\hline | | |
\lstinline^void put(actor_id, strong_actor_ptr)^ & Maps an actor to its ID. \\ +-------------------------------------------+-------------------------------------------------+
\hline | **Modifiers** | |
\lstinline^void erase(actor_id)^ & Removes an ID mapping from the registry. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``void put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. |
\lstinline^void put(atom_value, strong_actor_ptr)^ & Maps an actor to a name. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``void erase(actor_id)`` | Removes an ID mapping from the registry. |
\lstinline^void erase(atom_value)^ & Removes a name mapping from the registry. \\ +-------------------------------------------+-------------------------------------------------+
\hline | ``void put(atom_value, strong_actor_ptr)``| Maps an actor to a name. |
\end{tabular} +-------------------------------------------+-------------------------------------------------+
\end{center} | ``void erase(atom_value)`` | Removes a name mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+
\section{Remote Spawning of Actors \experimental} .. _remote-spawn:
\label{remote-spawn}
Remote Spawning of Actors :sup:`experimental`
==============================================
Remote spawning is an extension of the dynamic spawn using run-time type names Remote spawning is an extension of the dynamic spawn using run-time type names
\see{add-custom-actor-type}. The following example assumes a typed actor handle (see :ref:`add-custom-actor-type`). The following example assumes a typed actor
named \lstinline^calculator^ with an actor implementing this messaging handle named ``calculator`` with an actor implementing this messaging interface
interface named "calculator". named "calculator".
\cppexample[125-143]{remoting/remote_spawn} .. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 123-137
We first connect to a CAF node with \lstinline^middleman().connect(...)^. On We first connect to a CAF node with ``middleman().connect(...)``. On success,
success, \lstinline^connect^ returns the node ID we need for ``connect`` returns the node ID we need for ``remote_spawn``. This requires the
\lstinline^remote_spawn^. This requires the server to open a port with server to open a port with ``middleman().open(...)`` or
\lstinline^middleman().open(...)^ or \lstinline^middleman().publish(...)^. ``middleman().publish(...)``. Alternatively, we can obtain the node ID from an
Alternatively, we can obtain the node ID from an already existing remote actor already existing remote actor handle---returned from ``remote_actor`` for
handle---returned from \lstinline^remote_actor^ for example---via example---via ``hdl->node()``. After connecting to the server, we can use
\lstinline^hdl->node()^. After connecting to the server, we can use ``middleman().remote_spawn<...>(...)`` to create actors remotely.
\lstinline^middleman().remote_spawn<...>(...)^ to create actors remotely.
\section{Scheduler} .. _scheduler:
\label{scheduler}
Scheduler
=========
The CAF runtime maps N actors to M threads on the local machine. Applications The CAF runtime maps N actors to M threads on the local machine. Applications
build with CAF scale by decomposing tasks into many independent steps that are build with CAF scale by decomposing tasks into many independent steps that are
...@@ -12,13 +14,13 @@ Decomposing tasks implies that actors are often short-lived. Assigning a ...@@ -12,13 +14,13 @@ Decomposing tasks implies that actors are often short-lived. Assigning a
dedicated thread to each actor would not scale well. Instead, CAF includes a dedicated thread to each actor would not scale well. Instead, CAF includes a
scheduler that dynamically assigns actors to a pre-dimensioned set of worker scheduler that dynamically assigns actors to a pre-dimensioned set of worker
threads. Actors are modeled as lightweight state machines. Whenever a threads. Actors are modeled as lightweight state machines. Whenever a
\emph{waiting} actor receives a message, it changes its state to \emph{ready} *waiting* actor receives a message, it changes its state to *ready*
and is scheduled for execution. CAF cannot interrupt running actors because it and is scheduled for execution. CAF cannot interrupt running actors because it
is implemented in user space. Consequently, actors that use blocking system is implemented in user space. Consequently, actors that use blocking system
calls such as I/O functions can suspend threads and create an imbalance or lead calls such as I/O functions can suspend threads and create an imbalance or lead
to starvation. Such ``uncooperative'' actors can be explicitly detached by the to starvation. Such ``uncooperative'' actors can be explicitly detached by the
programmer by using the \lstinline^detached^ spawn option, e.g., programmer by using the ``detached`` spawn option, e.g.,
\lstinline^system.spawn<detached>(my_actor_fun)^. ``system.spawn<detached>(my_actor_fun)``.
The performance of actor-based applications depends on the scheduling algorithm The performance of actor-based applications depends on the scheduling algorithm
in use and its configuration. Different application scenarios require different in use and its configuration. Different application scenarios require different
...@@ -33,73 +35,80 @@ or an actor receives a message from a thread that is not under the control of ...@@ -33,73 +35,80 @@ or an actor receives a message from a thread that is not under the control of
the scheduler. Internal events are send and spawn operations from scheduled the scheduler. Internal events are send and spawn operations from scheduled
actors. actors.
\subsection{Policies} .. _scheduler-policy:
\label{scheduler-policy}
Policies
--------
The scheduler consists of a single coordinator and a set of workers. The The scheduler consists of a single coordinator and a set of workers. The
coordinator is needed by the public API to bridge actor and non-actor contexts, coordinator is needed by the public API to bridge actor and non-actor contexts,
but is not necessarily an active software entity. but is not necessarily an active software entity.
The scheduler of CAF is fully customizable by using a policy-based design. The The scheduler of CAF is fully customizable by using a policy-based design. The
following class shows a \emph{concept} class that lists all required member following class shows a *concept* class that lists all required member
types and member functions. A policy provides the two data structures types and member functions. A policy provides the two data structures
\lstinline^coordinator_data^ and \lstinline^worker_data^ that add additional ``coordinator_data`` and ``worker_data`` that add additional
data members to the coordinator and its workers respectively, e.g., work data members to the coordinator and its workers respectively, e.g., work
queues. This grants developers full control over the state of the scheduler. queues. This grants developers full control over the state of the scheduler.
\begin{lstlisting} .. code-block:: C++
struct scheduler_policy {
struct coordinator_data; struct scheduler_policy {
struct worker_data; struct coordinator_data;
void central_enqueue(Coordinator* self, resumable* job); struct worker_data;
void external_enqueue(Worker* self, resumable* job); void central_enqueue(Coordinator* self, resumable* job);
void internal_enqueue(Worker* self, resumable* job); void external_enqueue(Worker* self, resumable* job);
void resume_job_later(Worker* self, resumable* job); void internal_enqueue(Worker* self, resumable* job);
resumable* dequeue(Worker* self); void resume_job_later(Worker* self, resumable* job);
void before_resume(Worker* self, resumable* job); resumable* dequeue(Worker* self);
void after_resume(Worker* self, resumable* job); void before_resume(Worker* self, resumable* job);
void after_completion(Worker* self, resumable* job); void after_resume(Worker* self, resumable* job);
}; void after_completion(Worker* self, resumable* job);
\end{lstlisting} };
Whenever a new work item is scheduled---usually by sending a message to an idle Whenever a new work item is scheduled---usually by sending a message to an idle
actor---, one of the functions \lstinline^central_enqueue^, actor---, one of the functions ``central_enqueue``,
\lstinline^external_enqueue^, and \lstinline^internal_enqueue^ is called. The ``external_enqueue``, and ``internal_enqueue`` is called. The
first function is called whenever non-actor code interacts with the actor first function is called whenever non-actor code interacts with the actor
system. For example when spawning an actor from \lstinline^main^. Its first system. For example when spawning an actor from ``main``. Its first
argument is a pointer to the coordinator singleton and the second argument is argument is a pointer to the coordinator singleton and the second argument is
the new work item---usually an actor that became ready. The function the new work item---usually an actor that became ready. The function
\lstinline^external_enqueue^ is never called directly by CAF. It models the ``external_enqueue`` is never called directly by CAF. It models the
transfer of a task to a worker by the coordinator or another worker. Its first transfer of a task to a worker by the coordinator or another worker. Its first
argument is the worker receiving the new task referenced in the second argument is the worker receiving the new task referenced in the second
argument. The third function, \lstinline^internal_enqueue^, is called whenever argument. The third function, ``internal_enqueue``, is called whenever
an actor interacts with other actors in the system. Its first argument is the an actor interacts with other actors in the system. Its first argument is the
current worker and the second argument is the new work item. current worker and the second argument is the new work item.
Actors reaching the maximum number of messages per run are re-scheduled with Actors reaching the maximum number of messages per run are re-scheduled with
\lstinline^resume_job_later^ and workers acquire new work by calling ``resume_job_later`` and workers acquire new work by calling
\lstinline^dequeue^. The two functions \lstinline^before_resume^ and ``dequeue``. The two functions ``before_resume`` and
\lstinline^after_resume^ allow programmers to measure individual actor runtime, ``after_resume`` allow programmers to measure individual actor runtime,
while \lstinline^after_completion^ allows to execute custom code whenever a while ``after_completion`` allows to execute custom code whenever a
work item has finished execution by changing its state to \emph{done}, but work item has finished execution by changing its state to *done*, but
before it is destroyed. In this way, the last three functions enable developers before it is destroyed. In this way, the last three functions enable developers
to gain fine-grained insight into the scheduling order and individual execution to gain fine-grained insight into the scheduling order and individual execution
times. times.
\subsection{Work Stealing} .. _work-stealing:
\label{work-stealing}
Work Stealing
-------------
The default policy in CAF is work stealing. The key idea of this algorithm is The default policy in CAF is work stealing. The key idea of this algorithm is
to remove the bottleneck of a single, global work queue. The original to remove the bottleneck of a single, global work queue. The original
algorithm was developed for fully strict computations by Blumofe et al in 1994. algorithm was developed for fully strict computations by Blumofe et al in 1994.
It schedules any number of tasks to \lstinline^P^ workers, where \lstinline^P^ It schedules any number of tasks to ``P`` workers, where ``P``
is the number of processors available. is the number of processors available.
\singlefig{stealing}{Stealing of work items}{fig-stealing} .. _fig-stealing:
.. image:: stealing.png
:alt: Stealing of work items
Each worker dequeues work items from an individual queue until it is drained. Each worker dequeues work items from an individual queue until it is drained.
Once this happens, the worker becomes a \emph{thief}. It picks one of the other Once this happens, the worker becomes a *thief*. It picks one of the other
workers---usually at random---as a \emph{victim} and tries to \emph{steal} a workers---usually at random---as a *victim* and tries to *steal* a
work item. As a consequence, tasks (actors) are bound to workers by default and work item. As a consequence, tasks (actors) are bound to workers by default and
only migrate between threads as a result of stealing. This strategy minimizes only migrate between threads as a result of stealing. This strategy minimizes
communication between threads and maximizes cache locality. Work stealing has communication between threads and maximizes cache locality. Work stealing has
...@@ -114,24 +123,24 @@ or all? Since each worker has only local knowledge, it cannot decide when it ...@@ -114,24 +123,24 @@ or all? Since each worker has only local knowledge, it cannot decide when it
could safely suspend itself. Likewise, workers cannot resume if new job items could safely suspend itself. Likewise, workers cannot resume if new job items
arrived at one or more workers. For this reason, CAF uses three polling arrived at one or more workers. For this reason, CAF uses three polling
intervals. Once a worker runs out of work items, it tries to steal items from intervals. Once a worker runs out of work items, it tries to steal items from
others. First, it uses the \emph{aggressive} polling interval. It falls back to others. First, it uses the *aggressive* polling interval. It falls back to
a \emph{moderate} interval after a predefined number of trials. After another a *moderate* interval after a predefined number of trials. After another
predefined number of trials, it will finally use a \emph{relaxed} interval. predefined number of trials, it will finally use a *relaxed* interval.
Per default, the *aggressive* strategy performs 100 steal attempts with no sleep
interval in between. The *moderate* strategy tries to steal 500 times with 50
microseconds sleep between two steal attempts. Finally, the *relaxed* strategy
runs indefinitely but sleeps for 10 milliseconds between two attempts. These
defaults can be overridden via system config at startup (see
:ref:`system-config`).
Per default, the \emph{aggressive} strategy performs 100 steal attempts with no .. _work-sharing:
sleep interval in between. The \emph{moderate} strategy tries to steal 500
times with 50 microseconds sleep between two steal attempts. Finally, the
\emph{relaxed} strategy runs indefinitely but sleeps for 10 milliseconds
between two attempts. These defaults can be overridden via system config at
startup~\see{system-config}.
\subsection{Work Sharing} Work Sharing
\label{work-sharing} ------------
Work sharing is an alternative scheduler policy in CAF that uses a single, Work sharing is an alternative scheduler policy in CAF that uses a single,
global work queue. This policy uses a mutex and a condition variable on the global work queue. This policy uses a mutex and a condition variable on the
central queue. Thus, the policy supports only limited concurrency but does not central queue. Thus, the policy supports only limited concurrency but does not
need to poll. Using this policy can be a good fit for low-end devices where need to poll. Using this policy can be a good fit for low-end devices where
power consumption is an important metric. power consumption is an important metric.
% TODO: profiling section
\section{Streaming\experimental} .. _streaming:
\label{streaming}
Streaming :sup:`experimental`
=============================
Streams in CAF describe data flow between actors. We are not aiming to provide Streams in CAF describe data flow between actors. We are not aiming to provide
functionality similar to Apache projects like Spark, Flink or Storm. Likewise, functionality similar to Apache projects like Spark, Flink or Storm. Likewise,
...@@ -13,17 +15,23 @@ A stream establishes a logical channel between two or more actors for ...@@ -13,17 +15,23 @@ A stream establishes a logical channel between two or more actors for
exchanging a potentially unbound sequence of values. This channel uses demand exchanging a potentially unbound sequence of values. This channel uses demand
signaling to guarantee that senders cannot overload receivers. signaling to guarantee that senders cannot overload receivers.
\singlefig{stream}{Streaming Concept}{stream} .. _stream:
.. image:: stream.png
:alt: Streaming Concept
Streams are directed and data flows only \emph{downstream}, i.e., from sender Streams are directed and data flows only *downstream*, i.e., from sender
(source) to receiver (sink). Establishing a stream requires a handshake in (source) to receiver (sink). Establishing a stream requires a handshake in
order to initialize required state and signal initial demand. order to initialize required state and signal initial demand.
\singlefig{stream-roles}{Streaming Roles}{stream-roles} .. _stream-roles:
CAF distinguishes between three roles in a stream: (1) a \emph{source} creates .. image:: stream-roles.png
streams and generates data, (2) a \emph{stage} transforms or filters data, and :alt: Streaming Roles
(3) a \emph{sink} terminates streams by consuming data.
CAF distinguishes between three roles in a stream: (1) a *source* creates
streams and generates data, (2) a *stage* transforms or filters data, and
(3) a *sink* terminates streams by consuming data.
We usually draw streams as pipelines for simplicity. However, sources can have We usually draw streams as pipelines for simplicity. However, sources can have
any number of outputs (downstream actors). Likewise, sinks can have any number any number of outputs (downstream actors). Likewise, sinks can have any number
...@@ -31,19 +39,23 @@ of inputs (upstream actors) and stages can multiplex N inputs to M outputs. ...@@ -31,19 +39,23 @@ of inputs (upstream actors) and stages can multiplex N inputs to M outputs.
Hence, streaming topologies in CAF support arbitrary complexity with forks and Hence, streaming topologies in CAF support arbitrary complexity with forks and
joins. joins.
\subsection{Stream Managers} Stream Managers
---------------
Streaming-related messages are handled separately. Under the hood, actors Streaming-related messages are handled separately. Under the hood, actors
delegate to \emph{stream managers} that in turn allow customization of their delegate to *stream managers* that in turn allow customization of their
behavior with \emph{drivers} and \emph{downstream managers}. behavior with *drivers* and *downstream managers*.
.. _fig-stream-manager:
\singlefig{stream-manager}{Internals of Stream Managers}{fig-stream-manager} .. image:: stream-manager.png
:alt: Internals of Stream Managers
Users usually can skip implementing driver classes and instead use the Users usually can skip implementing driver classes and instead use the
lambda-based interface showcased in the following sections. Drivers implement lambda-based interface showcased in the following sections. Drivers implement
the streaming logic by taking inputs from upstream actors and pushing data to the streaming logic by taking inputs from upstream actors and pushing data to
the downstream manager. A source has no input buffer. Hence, drivers only the downstream manager. A source has no input buffer. Hence, drivers only
provide a \emph{generator} function that downstream managers call according to provide a *generator* function that downstream managers call according to
demand. demand.
A downstream manager is responsible for dispatching data to downstream actors. A downstream manager is responsible for dispatching data to downstream actors.
...@@ -52,49 +64,53 @@ the same data. The downstream manager can also perform any sort multi- or ...@@ -52,49 +64,53 @@ the same data. The downstream manager can also perform any sort multi- or
anycast. For example, a load-balancer would use an anycast policy to dispatch anycast. For example, a load-balancer would use an anycast policy to dispatch
data to the next available worker. data to the next available worker.
\clearpage Defining Sources
----------------
\subsection{Defining Sources} .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
\cppexample[17-46]{streaming/integer_stream} :lines: 17-48
The simplest way to defining a source is to use the The simplest way to defining a source is to use the
\lstinline^attach_stream_source^ function and pass it four arguments: a pointer ``attach_stream_source`` function and pass it four arguments: a pointer
to \emph{self}, \emph{initializer} for the state, \emph{generator} for to *self*, *initializer* for the state, *generator* for
producing values, and \emph{predicate} for signaling the end of the stream. producing values, and *predicate* for signaling the end of the stream.
\clearpage
\subsection{Defining Stages} Defining Stages
---------------
\cppexample[48-78]{streaming/integer_stream} .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 50-83
The function \lstinline^make_stage^ also takes three lambdas but additionally The function ``make_stage`` also takes three lambdas but additionally
the received input stream handshake as first argument. Instead of a predicate, the received input stream handshake as first argument. Instead of a predicate,
\lstinline^make_stage^ only takes a finalizer, since the stage does not produce ``make_stage`` only takes a finalizer, since the stage does not produce
data on its own and a stream terminates if no more sources exist. data on its own and a stream terminates if no more sources exist.
\clearpage Defining Sinks
--------------
\subsection{Defining Sinks} .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 85-114
\cppexample[80-106]{streaming/integer_stream} The function ``make_sink`` is similar to ``make_stage``, except
The function \lstinline^make_sink^ is similar to \lstinline^make_stage^, except
that is does not produce outputs. that is does not produce outputs.
\clearpage Initiating Streams
------------------
\subsection{Initiating Streams}
\cppexample[121-125]{streaming/integer_stream} .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 128-132
In our example, we always have a source \lstinline^int_source^ and a sink In our example, we always have a source ``int_source`` and a sink
\lstinline^int_sink^ with an optional stage \lstinline^int_selector^. Sending ``int_sink`` with an optional stage ``int_selector``. Sending
\lstinline^open_atom^ to the source initiates the stream and the source will ``open_atom`` to the source initiates the stream and the source will
respond with a stream handshake. respond with a stream handshake.
Using the actor composition in CAF (\lstinline^snk * src^ reads \emph{sink Using the actor composition in CAF (``snk * src`` reads *sink
after source}) allows us to redirect the stream handshake we send in after source*) allows us to redirect the stream handshake we send in
\lstinline^caf_main^ to the sink (or to the stage and then from the stage to ``caf_main`` to the sink (or to the stage and then from the stage to
the sink). the sink).
\section{Testing} .. _testing:
\label{testing}
Testing
=======
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
``disallow``.
\end{itemize} .. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++
:lines: 12-60
The following example implements two actors, \lstinline^ping^ and
\lstinline^pong^, that exchange a configurable amount of messages. The test
\emph{three pings} then checks the contents of each message with
\lstinline^expect^ and verifies that no additional messages exist using
\lstinline^disallow^.
\cppexample[12-65]{testing/ping_pong}
\section{Type Inspection (Serialization and String Conversion)} .. _type-inspection:
\label{type-inspection}
CAF is designed with distributed systems in mind. Hence, all message types Type Inspection (Serialization and String Conversion)
must be serializable and need a platform-neutral, unique name that is =====================================================
configured at startup \see{add-custom-message-type}. Using a message type that
is not serializable causes a compiler error \see{unsafe-message-type}. CAF CAF is designed with distributed systems in mind. Hence, all message types must
be serializable and need a platform-neutral, unique name that is configured at
startup (see :ref:`add-custom-message-type`). Using a message type that is not
serializable causes a compiler error (see :ref:`unsafe-message-type`). CAF
serializes individual elements of a message by using the inspection API. This serializes individual elements of a message by using the inspection API. This
API allows users to provide code for serialization as well as string conversion API allows users to provide code for serialization as well as string conversion
with a single free function. The signature for a class \lstinline^my_class^ is with a single free function. The signature for a class ``my_class`` is always as
always as follows: follows:
.. code-block:: C++
\begin{lstlisting} template <class Inspector>
template <class Inspector> typename Inspector::result_type inspect(Inspector& f, my_class& x) {
typename Inspector::result_type inspect(Inspector& f, my_class& x) { return f(...);
return f(...); }
}
\end{lstlisting}
The function \lstinline^inspect^ passes meta information and data fields to the The function ``inspect`` passes meta information and data fields to the
variadic call operator of the inspector. The following example illustrates an variadic call operator of the inspector. The following example illustrates an
implementation for \lstinline^inspect^ for a simple POD struct. implementation for ``inspect`` for a simple POD struct.
\cppexample[23-33]{custom_type/custom_types_1} .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 23-33
The inspector recursively inspects all data fields and has builtin support for The inspector recursively inspects all data fields and has builtin support for
(1) \lstinline^std::tuple^, (2) \lstinline^std::pair^, (3) C arrays, (4) any (1) ``std::tuple``, (2) ``std::pair``, (3) C arrays, (4) any
container type with \lstinline^x.size()^, \lstinline^x.empty()^, container type with ``x.size()``, ``x.empty()``,
\lstinline^x.begin()^ and \lstinline^x.end()^. ``x.begin()`` and ``x.end()``.
We consciously made the inspect API as generic as possible to allow for We consciously made the inspect API as generic as possible to allow for
extensibility. This allows users to use CAF's types in other contexts, to extensibility. This allows users to use CAF's types in other contexts, to
implement parsers, etc. implement parsers, etc.
\subsection{Inspector Concept} Inspector Concept
-----------------
The following concept class shows the requirements for inspectors. The The following concept class shows the requirements for inspectors. The
placeholder \lstinline^T^ represents any user-defined type. For example, placeholder ``T`` represents any user-defined type. For example,
\lstinline^error^ when performing I/O operations or some integer type when ``error`` when performing I/O operations or some integer type when
implementing a hash function. implementing a hash function.
\begin{lstlisting} .. code-block:: C++
Inspector {
using result_type = T;
if (inspector only requires read access to the state of T) Inspector {
static constexpr bool reads_state = true; using result_type = T;
else
static constexpr bool writes_state = true;
template <class... Ts> if (inspector only requires read access to the state of T)
result_type operator()(Ts&&...); static constexpr bool reads_state = true;
} else
\end{lstlisting} static constexpr bool writes_state = true;
A saving \lstinline^Inspector^ is required to handle constant lvalue and rvalue template <class... Ts>
references. A loading \lstinline^Inspector^ must only accept mutable lvalue result_type operator()(Ts&&...);
}
A saving ``Inspector`` is required to handle constant lvalue and rvalue
references. A loading ``Inspector`` must only accept mutable lvalue
references to data fields, but still allow for constant lvalue references and references to data fields, but still allow for constant lvalue references and
rvalue references to annotations. rvalue references to annotations.
\subsection{Annotations} Annotations
-----------
Annotations allow users to fine-tune the behavior of inspectors by providing Annotations allow users to fine-tune the behavior of inspectors by providing
addition meta information about a type. All annotations live in the namespace addition meta information about a type. All annotations live in the namespace
\lstinline^caf::meta^ and derive from \lstinline^caf::meta::annotation^. An ``caf::meta`` and derive from ``caf::meta::annotation``. An
inspector can query whether a type \lstinline^T^ is an annotation with inspector can query whether a type ``T`` is an annotation with
\lstinline^caf::meta::is_annotation<T>::value^. Annotations are passed to the ``caf::meta::is_annotation<T>::value``. Annotations are passed to the
call operator of the inspector along with data fields. The following list shows call operator of the inspector along with data fields. The following list shows
all annotations supported by CAF: all annotations supported by CAF:
\begin{itemize} * ``type_name(n)``: Display type name as ``n`` in human-friendly output (position before data fields).
\item \lstinline^type_name(n)^: Display type name as \lstinline^n^ in * ``hex_formatted()``: Format the following data field in hex format.
human-friendly output (position before data fields). * ``omittable()``: Omit the following data field in human-friendly output.
\item \lstinline^hex_formatted()^: Format the following data field in hex * ``omittable_if_empty()``: Omit the following data field if it is empty in human-friendly output.
format. * ``omittable_if_none()``: Omit the following data field if it equals ``none`` in human-friendly output.
\item \lstinline^omittable()^: Omit the following data field in human-friendly * ``save_callback(f)``: Call ``f`` when serializing (position after data fields).
output. * ``load_callback(f)``: Call ``f`` after deserializing all data fields (position after data fields).
\item \lstinline^omittable_if_empty()^: Omit the following data field if it is
empty in human-friendly output. Backwards and Third-party Compatibility
\item \lstinline^omittable_if_none()^: Omit the following data field if it ---------------------------------------
equals \lstinline^none^ in human-friendly output.
\item \lstinline^save_callback(f)^: Call \lstinline^f^ when serializing CAF evaluates common free function other than ``inspect`` in order to
(position after data fields).
\item \lstinline^load_callback(f)^: Call \lstinline^f^ after deserializing all
data fields (position after data fields).
\end{itemize}
\subsection{Backwards and Third-party Compatibility}
CAF evaluates common free function other than \lstinline^inspect^ in order to
simplify users to integrate CAF into existing code bases. simplify users to integrate CAF into existing code bases.
Serializers and deserializers call user-defined \lstinline^serialize^ Serializers and deserializers call user-defined ``serialize``
functions. Both types support \lstinline^operator&^ as well as functions. Both types support ``operator&`` as well as
\lstinline^operator()^ for individual data fields. A \lstinline^serialize^ ``operator()`` for individual data fields. A ``serialize``
function has priority over \lstinline^inspect^. function has priority over ``inspect``.
When converting a user-defined type to a string, CAF calls user-defined When converting a user-defined type to a string, CAF calls user-defined
\lstinline^to_string^ functions and prefers those over \lstinline^inspect^. ``to_string`` functions and prefers those over ``inspect``.
.. _unsafe-message-type:
\subsection{Whitelisting Unsafe Message Types} Whitelisting Unsafe Message Types
\label{unsafe-message-type} ---------------------------------
Message types that are not serializable cause compile time errors when used in Message types that are not serializable cause compile time errors when used in
actor communication. When using CAF for concurrency only, this errors can be actor communication. When using CAF for concurrency only, this errors can be
suppressed by whitelisting types with suppressed by whitelisting types with
\lstinline^CAF_ALLOW_UNSAFE_MESSAGE_TYPE^. The macro is defined as follows. ``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The macro is defined as follows.
% TODO: expand example here\cppexample[linerange={50-54}]{../../libcaf_core/caf/allowed_unsafe_message_type.hpp}
\clearpage Splitting Save and Load Operations
\subsection{Splitting Save and Load Operations} ----------------------------------
If loading and storing cannot be implemented in a single function, users can If loading and storing cannot be implemented in a single function, users can
query whether the inspector is loading or storing. For example, consider the query whether the inspector is loading or storing. For example, consider the
following class \lstinline^foo^ with getter and setter functions and no public following class ``foo`` with getter and setter functions and no public
access to its members. access to its members.
\cppexample[20-49]{custom_type/custom_types_3} .. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++
:lines: 20-49
\clearpage Since there is no access to the data fields ``a_`` and ``b_``
Since there is no access to the data fields \lstinline^a_^ and \lstinline^b_^ (and assuming no changes to ``foo`` are possible), we need to split our
(and assuming no changes to \lstinline^foo^ are possible), we need to split our implementation of ``inspect`` as shown below.
implementation of \lstinline^inspect^ as shown below.
\cppexample[76-103]{custom_type/custom_types_3} .. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++
:lines: 76-103
The purpose of the scope guard in the example above is to write the content of The purpose of the scope guard in the example above is to write the content of
the temporaries back to \lstinline^foo^ at scope exit automatically. Storing the temporaries back to ``foo`` at scope exit automatically. Storing
the result of \lstinline^f(...)^ in a temporary first and then writing the the result of ``f(...)`` in a temporary first and then writing the
changes to \lstinline^foo^ is not possible, because \lstinline^f(...)^ can changes to ``foo`` is not possible, because ``f(...)`` can
return \lstinline^void^. return ``void``.
\section{Using \texttt{aout} -- A Concurrency-safe Wrapper for \texttt{cout}} Using ``aout`` -- A Concurrency-safe Wrapper for ``cout``
=========================================================
When using \lstinline^cout^ from multiple actors, output often appears When using ``cout`` from multiple actors, output often appears
interleaved. Moreover, using \lstinline^cout^ from multiple actors -- and thus interleaved. Moreover, using ``cout`` from multiple actors -- and thus
from multiple threads -- in parallel should be avoided regardless, since the from multiple threads -- in parallel should be avoided regardless, since the
standard does not guarantee a thread-safe implementation. standard does not guarantee a thread-safe implementation.
By replacing \texttt{std::cout} with \texttt{caf::aout}, actors can achieve a By replacing ``std::cout`` with ``caf::aout``, actors can achieve a
concurrency-safe text output. The header \lstinline^caf/all.hpp^ also defines concurrency-safe text output. The header ``caf/all.hpp`` also defines overloads
overloads for \texttt{std::endl} and \texttt{std::flush} for \lstinline^aout^, for ``std::endl`` and ``std::flush`` for ``aout``, but does not support the full
but does not support the full range of ostream operations (yet). Each write range of ostream operations (yet). Each write operation to ``aout`` sends a
operation to \texttt{aout} sends a message to a `hidden' actor. This actor only message to a "hidden" actor. This actor only prints lines, unless output is
prints lines, unless output is forced using \lstinline^flush^. The example forced using ``flush``. The example below illustrates printing of lines of text
below illustrates printing of lines of text from multiple actors (in random from multiple actors (in random order).
order).
.. literalinclude:: /examples/aout.cpp
:language: C++
\cppexample{aout}
\section{Utility} .. _utility:
\label{utility}
Utility
=======
CAF includes a few utility classes that are likely to be part of C++ CAF includes a few utility classes that are likely to be part of C++
eventually (or already are in newer versions of the standard). However, until eventually (or already are in newer versions of the standard). However, until
these classes are part of the standard library on all supported compilers, we these classes are part of the standard library on all supported compilers, we
unfortunately have to maintain our own implementations. unfortunately have to maintain our own implementations.
\subsection{Class \lstinline^optional^} .. _optional:
\label{optional}
Represents a value that may or may not exist. Class ``optional``
------------------
\begin{center} Represents a value that may or may not exist.
\begin{tabular}{ll}
\textbf{Constructors} & ~ \\
\hline
\lstinline^(T value)^ & Constructs an object with a value. \\
\hline
\lstinline^(none_t = none)^ & Constructs an object without a value. \\
\hline
~ & ~ \\ \textbf{Observers} & ~ \\
\hline
\lstinline^explicit operator bool()^ & Checks whether the object contains a value. \\
\hline
\lstinline^T* operator->()^ & Accesses the contained value. \\
\hline
\lstinline^T& operator*()^ & Accesses the contained value. \\
\hline
\end{tabular}
\end{center}
\subsection{Class \lstinline^expected^} +-----------------------------+---------------------------------------------+
| **Constructors** | |
+-----------------------------+---------------------------------------------+
| ``(T value)`` | Constructs an object with a value. |
+-----------------------------+---------------------------------------------+
| ``(none_t = none)`` | Constructs an object without a value. |
+-----------------------------+---------------------------------------------+
| | |
+-----------------------------+---------------------------------------------+
| **Observers** | |
+-----------------------------+---------------------------------------------+
| ``explicit operator bool()``| Checks whether the object contains a value. |
+-----------------------------+---------------------------------------------+
| ``T* operator->()`` | Accesses the contained value. |
+-----------------------------+---------------------------------------------+
| ``T& operator*()`` | Accesses the contained value. |
+-----------------------------+---------------------------------------------+
Represents the result of a computation that \emph{should} return a value. If no Class ``expected``
value could be produced, the \lstinline^expected<T>^ contains an ------------------
\lstinline^error^ \see{error}.
\begin{center} Represents the result of a computation that *should* return a value. If no value
\begin{tabular}{ll} could be produced, the ``expected<T>`` contains an ``error`` (see :ref:`error`).
\textbf{Constructors} & ~ \\
\hline
\lstinline^(T value)^ & Constructs an object with a value. \\
\hline
\lstinline^(error err)^ & Constructs an object with an error. \\
\hline
~ & ~ \\ \textbf{Observers} & ~ \\
\hline
\lstinline^explicit operator bool()^ & Checks whether the object contains a value. \\
\hline
\lstinline^T* operator->()^ & Accesses the contained value. \\
\hline
\lstinline^T& operator*()^ & Accesses the contained value. \\
\hline
\lstinline^error& error()^ & Accesses the contained error. \\
\hline
\end{tabular}
\end{center}
+-----------------------------+---------------------------------------------+
| **Constructors** | |
+-----------------------------+---------------------------------------------+
| ``(T value)`` | Constructs an object with a value. |
+-----------------------------+---------------------------------------------+
| ``(error err)`` | Constructs an object with an error. |
+-----------------------------+---------------------------------------------+
| | |
+-----------------------------+---------------------------------------------+
| **Observers** | |
+-----------------------------+---------------------------------------------+
| ``explicit operator bool()``| Checks whether the object contains a value. |
+-----------------------------+---------------------------------------------+
| ``T* operator->()`` | Accesses the contained value. |
+-----------------------------+---------------------------------------------+
| ``T& operator*()`` | Accesses the contained value. |
+-----------------------------+---------------------------------------------+
| ``error& error()`` | Accesses the contained error. |
+-----------------------------+---------------------------------------------+
\subsection{Constant \lstinline^unit^} Constant ``unit``
-----------------
The constant \lstinline^unit^ of type \lstinline^unit_t^ is the equivalent of The constant ``unit`` of type ``unit_t`` is the equivalent of
\lstinline^void^ and can be used to initialize \lstinline^optional<void>^ and ``void`` and can be used to initialize ``optional<void>`` and
\lstinline^expected<void>^. ``expected<void>``.
\subsection{Constant \lstinline^none^} Constant ``none``
-----------------
The constant \lstinline^none^ of type \lstinline^none_t^ can be used to The constant ``none`` of type ``none_t`` can be used to
initialize an \lstinline^optional<T>^ to represent ``nothing''. initialize an ``optional<T>`` to represent "nothing".
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment