Commit f15f5afc authored by Dominik Charousset's avatar Dominik Charousset

Convert manual to reStructuredText

(cherry picked from commit 5c274689)
parent 4e96bbf2
This diff is collapsed.
This diff is collapsed.
\section{Common Pitfalls}
\label{pitfalls}
.. _pitfalls:
Common Pitfalls
===============
This Section highlights common mistakes or C++ subtleties that can show up when
programming in CAF.
\subsection{Defining Message Handlers}
Defining Message Handlers
-------------------------
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
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 = (
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
\end{lstlisting}
message_handler wrong = (
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
The correct way to initialize message handlers and behaviors is to either
use the constructor or the member function \lstinline^assign^:
\begin{lstlisting}
message_handler ok1{
[](int i) { /*...*/ },
[](float f) { /*...*/ }
};
message_handler ok2;
// some place later
ok2.assign(
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
\end{lstlisting}
\subsection{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.
use the constructor or the member function ``assign``:
.. code-block:: C++
message_handler ok1{
[](int i) { /*...*/ },
[](float f) { /*...*/ }
};
message_handler ok2;
// some place later
ok2.assign(
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
Event-Based API
---------------
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
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.
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.
\clearpage
\subsection{Sharing}
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
another actor. Accessing shared memory segments concurrently can cause undefined
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
the lifetime guarantee is by storing the data in smart pointers such as
\lstinline^std::shared_ptr^. Nevertheless, the recommended way of sharing
informations is message passing. Sending the same message to multiple actors
``std::shared_ptr``. Nevertheless, the recommended way of sharing
information is message passing. Sending the same message to multiple actors
does not result in copying the data several times.
This diff is collapsed.
\section{Errors}
\label{error}
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
plattform-neutral and serializable. Instead of using category singletons, CAF
stores categories as atoms~\see{atom}. Errors can also include a message to
provide additional context information.
\subsection{Class Interface}
\begin{center}
\begin{tabular}{ll}
\textbf{Constructors} & ~ \\
\hline
\lstinline^(Enum x)^ & Construct error by calling \lstinline^make_error(x)^ \\
\hline
\lstinline^(uint8_t x, atom_value y)^ & Construct error with code \lstinline^x^ and category \lstinline^y^ \\
\hline
\lstinline^(uint8_t x, atom_value y, message z)^ & Construct error with code \lstinline^x^, category \lstinline^y^, and context \lstinline^z^ \\
\hline
~ & ~ \\ \textbf{Observers} & ~ \\
\hline
\lstinline^uint8_t code()^ & Returns the error code \\
\hline
\lstinline^atom_value category()^ & Returns the error category \\
\hline
\lstinline^message context()^ & Returns additional context information \\
\hline
\lstinline^explicit operator bool()^ & Returns \lstinline^code() != 0^ \\
\hline
\end{tabular}
\end{center}
\subsection{Add Custom Error Categories}
\label{custom-error}
Adding custom error categories requires three steps: (1)~declare an enum class
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
a render function. The last step is optional to allow users to retrieve a
better string representation from \lstinline^system.render(x)^ than
\lstinline^to_string(x)^ can offer. Note that any error code with value 0 is
interpreted as \emph{not-an-error}. The following example adds a custom error
category by performing the first two steps.
\cppexample[19-34]{message_passing/divider}
The implementation of \lstinline^to_string(error)^ is unable to call string
conversions for custom error categories. Hence,
\lstinline^to_string(make_error(math_error::division_by_zero))^ returns
\lstinline^"error(1, math)"^.
The following code adds a rendering function to the actor system to provide a
more satisfactory string conversion.
\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
.. _error:
Errors
======
Errors in CAF have a code and a category, similar to ``std::error_code`` and
``std::error_condition``. Unlike its counterparts from the C++ standard library,
``error`` is plattform-neutral and serializable. Instead of using category
singletons, CAF stores categories as atoms (see :ref:`atom`). Errors can also
include a message to provide additional context information.
Class Interface
---------------
+-----------------------------------------+--------------------------------------------------------------------+
| **Constructors** | |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(Enum x)`` | Construct error by calling ``make_error(x)`` |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y, message z)``| Construct error with code ``x``, category ``y``, and context ``z`` |
+-----------------------------------------+--------------------------------------------------------------------+
| | |
+-----------------------------------------+--------------------------------------------------------------------+
| **Observers** | |
+-----------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code |
+-----------------------------------------+--------------------------------------------------------------------+
| ``atom_value category()`` | Returns the error category |
+-----------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+
| ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+
.. _custom-error:
Add Custom Error Categories
---------------------------
Adding custom error categories requires three steps: (1) declare an enum class
of type ``uint8_t`` with the first value starting at 1, (2) specialize
``error_category`` to give your type a custom ID (value 0-99 are
reserved by CAF), and (3) add your error category to the actor system config.
The following example adds custom error codes for math errors.
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:lines: 17-47
.. _sec:
System Error Codes
------------------
System Error Codes (SECs) use the error category ``"system"``. They
represent errors in the actor system or one of its modules and are defined as
follows.
\sourcefile[32-117]{libcaf_core/caf/sec.hpp}
.. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++
:lines: 32-117
%\clearpage
\subsection{Default Exit Reasons}
\label{exit-reason}
.. _exit-reason:
CAF uses the error category \lstinline^"exit"^ for default exit reasons. These
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,
user-requested shutdown and can be used by programmers in the same way. The
latter terminates an actor unconditionally when used in \lstinline^send_exit^,
even if the default handler for exit messages~\see{exit-message} is overridden.
Default Exit Reasons
--------------------
CAF uses the error category ``"exit"`` for default exit reasons. These errors
are usually fail states set by the actor system itself. The two exceptions are
``exit_reason::user_shutdown`` and ``exit_reason::kill``. The former is used in
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}
\label{faq}
.. _faq:
Frequently Asked Questions
==========================
This Section is a compilation of the most common questions via GitHub, chat,
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.
Usually, messages are created implicitly when sending messages but can also be
created explicitly using \lstinline^make_message^. In both cases, types and
number of elements are known at compile time. To allow for fully dynamic
message generation, CAF also offers \lstinline^message_builder^:
\begin{lstlisting}
message_builder mb;
// prefix message with some atom
mb.append(strings_atom::value);
// fill message with some strings
std::vector<std::string> strings{/*...*/};
for (auto& str : strings)
mb.append(str);
// create the message
message msg = mb.to_message();
\end{lstlisting}
\subsection{What Debugging Tools Exist?}
The \lstinline^scripts/^ and \lstinline^tools/^ directories contain some useful
tools to aid in development and debugging.
\lstinline^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
\lstinline^atom_constant<...>^ with a human-readable representation of the
created explicitly using ``make_message``. In both cases, types and number of
elements are known at compile time. To allow for fully dynamic message
generation, CAF also offers ``message_builder``:
.. code-block:: C++
message_builder mb;
// prefix message with some atom
mb.append(strings_atom::value);
// fill message with some strings
std::vector<std::string> strings{/*...*/};
for (auto& str : strings)
mb.append(str);
// create the message
message msg = mb.to_message();
What Debugging Tools Exist?
---------------------------
The ``scripts/`` and ``tools/`` directories contain some useful tools to aid in
development and debugging.
``scripts/atom.py`` converts integer atom values back into strings.
``scripts/demystify.py`` replaces cryptic ``typed_mpi<...>``
templates with ``replies_to<...>::with<...>`` and
``atom_constant<...>`` with a human-readable representation of the
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.
\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
visualization via \href{https://bestchai.bitbucket.io/shiviz/}{ShiViz}.
visualization via `ShiViz <https://bestchai.bitbucket.io/shiviz/>`_.
\section{Group Communication}
\label{groups}
.. _groups:
Group Communication
===================
CAF supports publish/subscribe-based group communication. Dynamically typed
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
name, joining, and leaving.
\begin{lstlisting}
std::string module = "local";
std::string id = "foo";
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
std::cerr << "*** cannot load group: "
<< system.render(expected_grp.error()) << std::endl;
return;
}
auto grp = std::move(*expected_grp);
scoped_actor self{system};
self->join(grp);
self->send(grp, "test");
self->receive(
[](const std::string& str) {
assert(str == "test");
}
);
self->leave(grp);
\end{lstlisting}
.. code-block:: C++
std::string module = "local";
std::string id = "foo";
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
std::cerr << "*** cannot load group: "
<< system.render(expected_grp.error()) << std::endl;
return;
}
auto grp = std::move(*expected_grp);
scoped_actor self{system};
self->join(grp);
self->send(grp, "test");
self->receive(
[](const std::string& str) {
assert(str == "test");
}
);
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
the group. However, local modules can be easier accessed by calling
\lstinline`system.groups().get_local(id)`, which returns \lstinline`group`
instead of \lstinline`expected<group>`.
``system.groups().get_local(id)``, which returns ``group``
instead of ``expected<group>``.
.. _anonymous-group:
\subsection{Anonymous Groups}
\label{anonymous-group}
Anonymous Groups
----------------
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,
unique group instance.
\subsection{Local Groups}
\label{local-group}
.. _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
by \lstinline^system.groups().get_local("GUI events")^. The group ID
\lstinline^"GUI events"^ uniquely identifies a singleton group instance of the
module \lstinline^"local"^.
by ``system.groups().get_local("GUI events")``. The group ID
``"GUI events"`` uniquely identifies a singleton group instance of the
module ``"local"``.
.. _remote-group:
\subsection{Remote Groups}
\label{remote-group}
Remote Groups
-------------
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
denotes the port, while the second (optional) parameter can be used to
whitelist IP addresses.
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:
\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
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
explain the terminology used in this manual.
\subsection{Actor Model}
Actor Model
-----------
The actor model describes concurrent entities---actors---that do not share
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
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
development of concurrent as well as distributed systems. In this regard, CAF
follows the C++ philosophy ``building the highest abstraction possible without
sacrificing performance''.
follows the C++ philosophy *building the highest abstraction possible
without sacrificing performance*.
\subsection{Terminology}
Terminology
-----------
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
......@@ -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
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
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
is (usually) faster prototyping and less code. This comes at the cost of
requiring excessive testing.
\subsubsection{Statically Typed Actor}
Statically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~
CAF achieves static type-checking for actors by defining abstract messaging
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
at an increase in required source code, as developers have to define and use
messaging interfaces.
\subsubsection{Actor References}
\label{actor-reference}
.. _actor-reference:
Actor References
~~~~~~~~~~~~~~~~
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
not refer to a \emph{memory region} in this context.
an actor are addresses, handles, and pointers. Note that *address* does
not refer to a *memory region* in this context.
.. _actor-address:
\paragraph{Address}
\label{actor-address}
Address
+++++++
Each actor has a (network-wide) unique logical address. This identifier is
represented by \lstinline^actor_addr^, which allows to identify and monitor an
actor. Unlike other actor frameworks, CAF does \emph{not} allow users to send
messages to addresses. This limitation is due to the fact that the address does
not contain any type information. Hence, it would not be safe to send it a
message, because the receiving actor might use a statically typed interface
that does not accept the given message. Because an \lstinline^actor_addr^ fills
the role of an identifier, it has \emph{weak reference semantics}
\see{reference-counting}.
\paragraph{Handle}
\label{actor-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
between handles and addresses---which is unique to CAF when comparing it to
other actor systems---is a consequence of the design decision to enforce static
type checking for all messages. Dynamically typed actors use \lstinline^actor^
handles, while statically typed actors use \lstinline^typed_actor<...>^
handles. Both types have \emph{strong reference semantics}
\see{reference-counting}.
\paragraph{Pointer}
\label{actor-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
\lstinline^actor_cast^ \see{actor-cast} prior to sending messages. A
\lstinline^strong_actor_ptr^ can be \emph{null}.
\subsubsection{Spawning}
\emph{Spawning} an actor means to create and run a new actor.
\subsubsection{Monitor}
\label{monitor}
A monitored actor sends a down message~\see{down-message} to all actors
represented by ``actor_addr``, which allows to identify and monitor an actor.
Unlike other actor frameworks, CAF does *not* allow users to send messages to
addresses. This limitation is due to the fact that the address does not contain
any type information. Hence, it would not be safe to send it a message, because
the receiving actor might use a statically typed interface that does not accept
the given message. Because an ``actor_addr`` fills the role of an identifier, it
has *weak reference semantics* (see :ref:`reference-counting`).
.. _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 between handles
and addresses---which is unique to CAF when comparing it to other actor
systems---is a consequence of the design decision to enforce static type
checking for all messages. Dynamically typed actors use ``actor`` handles, while
statically typed actors use ``typed_actor<...>`` handles. Both types have
*strong reference semantics* (see :ref:`reference-counting`).
.. _actor-pointer:
Pointer
+++++++
In a few instances, CAF uses ``strong_actor_ptr`` to refer to an actor using
*strong reference semantics* (see :ref:`reference-counting`) without knowing the
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
*null*.
Spawning
~~~~~~~~
*Spawning* an actor means to create and run a new actor.
.. _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
actors and to take actions when one of the supervised actors fails, i.e.,
terminates with a non-normal exit reason.
\subsubsection{Link}
\label{link}
.. _link:
Link
~~~~
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.
Unlike down messages, exit messages cause the receiving actor to terminate as
well when receiving a non-normal exit reason per default. This allows
developers to create a set of actors with the guarantee that either all or no
actors are alive. Actors can override the default handler to implement error
recovery strategies.
\subsection{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
can come with breaking changes to the API or even remove a feature completely.
However, we encourage developers to extensively test such 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.
exit message (see :ref:`exit-message`) to all of its links as part of its
termination. Unlike down messages, exit messages cause the receiving actor to
terminate as well when receiving a non-normal exit reason per default. This
allows developers to create a set of actors with the guarantee that either all
or no actors are alive. Actors can override the default handler to implement
error recovery strategies.
Experimental Features
---------------------
Sections that discuss experimental features are highlighted with
:sup:`experimental`. The API of such features is not stable. This means even
minor updates to CAF can come with breaking changes to the API or even remove a
feature completely. However, we encourage developers to extensively test such
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}
\label{worker-groups}
.. _worker-groups:
Managing Groups of Workers :sup:`experimental`
===============================================
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
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
function for workers, and dispatching policy). After construction, one can add
new workers via messages of the form \texttt{('SYS', 'PUT', worker)}, remove
workers with \texttt{('SYS', 'DELETE', worker)}, and retrieve the set of
workers as \lstinline^vector<actor>^ via \texttt{('SYS', 'GET')}.
new workers via messages of the form ``('SYS', 'PUT', worker)``, remove
workers with ``('SYS', 'DELETE', worker)``, and retrieve the set of
workers as ``vector<actor>`` via ``('SYS', 'GET')``.
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
......@@ -22,61 +24,62 @@ Consequently, a terminating worker loses all unprocessed messages. For more
advanced caching strategies, such as reliable message delivery, users can
implement their own dispatching policies.
\subsection{Dispatching Policies}
Dispatching Policies
--------------------
A dispatching policy is a functor with the following signature:
\begin{lstlisting}
using uplock = upgrade_lock<detail::shared_spinlock>;
using policy = std::function<void (actor_system& sys,
uplock& guard,
const actor_vec& workers,
mailbox_element_ptr& ptr,
execution_unit* host)>;
\end{lstlisting}
.. code-block:: C++
using uplock = upgrade_lock<detail::shared_spinlock>;
using policy = std::function<void (actor_system& sys,
uplock& guard,
const actor_vec& workers,
mailbox_element_ptr& ptr,
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
vector containing all workers managed by the pool. The argument \lstinline^ptr^
contains the full message as received by the pool. Finally, \lstinline^host^ is
vector containing all workers managed by the pool. The argument ``ptr``
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
corresponding job queue.
The actor pool class comes with a set predefined policies, accessible via
factory functions, for convenience.
\begin{lstlisting}
actor_pool::policy actor_pool::round_robin();
\end{lstlisting}
.. code-block:: C++
actor_pool::policy actor_pool::round_robin();
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
the worker exits before processing all of its messages.
\begin{lstlisting}
actor_pool::policy actor_pool::broadcast();
\end{lstlisting}
.. code-block:: C++
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
recognize the first arriving response message---or error---and discard
subsequent messages. Note that this is not caused by the policy itself, but a
consequence of forwarding synchronous messages to more than one actor.
\begin{lstlisting}
actor_pool::policy actor_pool::random();
\end{lstlisting}
.. code-block:: C++
actor_pool::policy actor_pool::random();
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.
\begin{lstlisting}
using join = function<void (T&, message&)>;
using split = function<void (vector<pair<actor, message>>&, message&)>;
template <class T>
static policy split_join(join jf, split sf = ..., T init = T());
\end{lstlisting}
.. code-block:: C++
using join = function<void (T&, message&)>;
using split = function<void (vector<pair<actor, message>>&, message&)>;
template <class T>
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
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.
The join function is responsible for ``glueing'' all result messages together
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.
The first argument of a split function is a mapping from actors (workers) to
......
\section{Message Handlers}
\label{message-handler}
.. _message-handler:
Message Handlers
================
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.
\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
change its behavior when not receiving message after a certain amount of time.
\begin{lstlisting}
message_handler x1{
[](int i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ }
};
\end{lstlisting}
In our first example, \lstinline^x1^ models a behavior accepting messages that
consist of either exactly one \lstinline^int^, or one \lstinline^double^, or
three \lstinline^int^ values. Any other message is not matched and gets
forwarded to the default handler \see{default-handler}.
\begin{lstlisting}
message_handler x2{
[](double db) { /*...*/ },
[](double db) { /* - unreachable - */ }
};
\end{lstlisting}
.. code-block:: C++
message_handler x1{
[](int i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ }
};
In our first example, ``x1`` models a behavior accepting messages that consist
of either exactly one ``int``, or one ``double``, or three ``int`` values. Any
other message is not matched and gets forwarded to the default handler (see
:ref:`default-handler`).
.. code-block:: C++
message_handler x2{
[](double db) { /*...*/ },
[](double db) { /* - unreachable - */ }
};
Our second example illustrates an important characteristic of the matching
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
\lstinline^x2^ is unreachable.
``x2`` is unreachable.
.. code-block:: C++
\begin{lstlisting}
message_handler x3 = x1.or_else(x2);
message_handler x4 = x2.or_else(x1);
\end{lstlisting}
message_handler x3 = x1.or_else(x2);
message_handler x4 = x2.or_else(x1);
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
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,
\lstinline^x3^ behaves exactly like \lstinline^x1^. This is because the second
callback in \lstinline^x1^ will consume any message with a single
\lstinline^double^ and both callbacks in \lstinline^x2^ are thus unreachable.
The handler \lstinline^x4^ will consume messages with a single
\lstinline^double^ using the first callback in \lstinline^x2^, essentially
overriding the second callback in \lstinline^x1^.
``x3`` behaves exactly like ``x1``. This is because the second
callback in ``x1`` will consume any message with a single
``double`` and both callbacks in ``x2`` are thus unreachable.
The handler ``x4`` will consume messages with a single
``double`` using the first callback in ``x2``, essentially
overriding the second callback in ``x1``.
\clearpage
\subsection{Atoms}
\label{atom}
.. _atom:
Atoms
-----
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
......@@ -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
operation must be encoded into the message. The Erlang programming language
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.
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
characters and prohibits special characters. Legal characters are
\lstinline^_0-9A-Za-z^ and the whitespace character. Atoms are created using
the \lstinline^constexpr^ function \lstinline^atom^, as the following example
``_0-9A-Za-z`` and the whitespace character. Atoms are created using
the ``constexpr`` function ``atom``, as the following example
illustrates.
\begin{lstlisting}
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
\end{lstlisting}
.. code-block:: C++
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
\textbf{Warning}: The compiler cannot enforce the restrictions at compile time,
except for a length check. The assertion \lstinline^atom("!?") != atom("?!")^
**Warning**: The compiler cannot enforce the restrictions at compile time,
except for a length check. The assertion ``atom("!?") != atom("?!")``
is not true, because each invalid character translates to a whitespace
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
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 multiply_atom = atom_constant<atom("multiply")>;
\end{lstlisting}
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
Using these constants, we can now define message passing interfaces in a
convenient way:
\begin{lstlisting}
behavior do_math{
[](add_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}
Atom constants define a static member \lstinline^value^. Please note that this
static \lstinline^value^ member does \emph{not} have the type
\lstinline^atom_value^, unlike \lstinline^std::integral_constant^ for example.
.. code-block:: C++
behavior do_math{
[](add_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)
Atom constants define a static member ``value``. Please note that this
static ``value`` member does *not* have the type
``atom_value``, unlike ``std::integral_constant`` for example.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
\section{Overview}
Overview
========
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:
\begin{verbatim}
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
\end{verbatim}
.. ::
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
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)
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}
\item Lightweight, fast and efficient actor implementations
\item Network transparent messaging
\item Error handling based on Erlang's failure model
\item Pattern matching for messages as internal DSL to ease development
\item Thread-mapped actors for soft migration of existing applications
\item Publish/subscribe group communication
\end{itemize}
* Lightweight, fast and efficient actor implementations
* Network transparent messaging
* Error handling based on Erlang's failure model
* Pattern matching for messages as internal DSL to ease development
* Thread-mapped actors for soft migration of existing applications
* Publish/subscribe group communication
Minimal Compiler Versions
-------------------------
\subsection{Minimal Compiler Versions}
* GCC 4.8
* Clang 3.4
* Visual Studio 2015, Update 3
\begin{itemize}
\item GCC 4.8
\item Clang 3.4
\item Visual Studio 2015, Update 3
\end{itemize}
Supported Operating Systems
---------------------------
\subsection{Supported Operating Systems}
* Linux
* Mac OS X
* Windows (static library only)
\begin{itemize}
\item Linux
\item Mac OS X
\item Windows (static library only)
\end{itemize}
Hello World Example
-------------------
\clearpage
\subsection{Hello World Example}
.. literalinclude:: /examples/hello_world.cpp
:language: C++
\cppexample{hello_world}
This diff is collapsed.
This diff is collapsed.
\section{Remote Spawning of Actors \experimental}
\label{remote-spawn}
.. _remote-spawn:
Remote Spawning of Actors :sup:`experimental`
==============================================
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
named \lstinline^calculator^ with an actor implementing this messaging
interface named "calculator".
(see :ref:`add-custom-actor-type`). The following example assumes a typed actor
handle named ``calculator`` with an actor implementing this messaging interface
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
success, \lstinline^connect^ returns the node ID we need for
\lstinline^remote_spawn^. This requires the server to open a port with
\lstinline^middleman().open(...)^ or \lstinline^middleman().publish(...)^.
Alternatively, we can obtain the node ID from an already existing remote actor
handle---returned from \lstinline^remote_actor^ for example---via
\lstinline^hdl->node()^. After connecting to the server, we can use
\lstinline^middleman().remote_spawn<...>(...)^ to create actors remotely.
We first connect to a CAF node with ``middleman().connect(...)``. On success,
``connect`` returns the node ID we need for ``remote_spawn``. This requires the
server to open a port with ``middleman().open(...)`` or
``middleman().publish(...)``. Alternatively, we can obtain the node ID from an
already existing remote actor handle---returned from ``remote_actor`` for
example---via ``hdl->node()``. After connecting to the server, we can use
``middleman().remote_spawn<...>(...)`` to create actors remotely.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
\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
interleaved. Moreover, using \lstinline^cout^ from multiple actors -- and thus
When using ``cout`` from multiple actors, output often appears
interleaved. Moreover, using ``cout`` from multiple actors -- and thus
from multiple threads -- in parallel should be avoided regardless, since the
standard does not guarantee a thread-safe implementation.
By replacing \texttt{std::cout} with \texttt{caf::aout}, actors can achieve a
concurrency-safe text output. The header \lstinline^caf/all.hpp^ also defines
overloads for \texttt{std::endl} and \texttt{std::flush} for \lstinline^aout^,
but does not support the full range of ostream operations (yet). Each write
operation to \texttt{aout} sends a message to a `hidden' actor. This actor only
prints lines, unless output is forced using \lstinline^flush^. The example
below illustrates printing of lines of text from multiple actors (in random
order).
By replacing ``std::cout`` with ``caf::aout``, actors can achieve a
concurrency-safe text output. The header ``caf/all.hpp`` also defines overloads
for ``std::endl`` and ``std::flush`` for ``aout``, but does not support the full
range of ostream operations (yet). Each write operation to ``aout`` sends a
message to a "hidden" actor. This actor only prints lines, unless output is
forced using ``flush``. The example below illustrates printing of lines of text
from multiple actors (in random order).
.. literalinclude:: /examples/aout.cpp
:language: C++
\cppexample{aout}
This diff is collapsed.
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