@@ -9,23 +9,10 @@ Context-switching actors are used for actors that make use of the blocking API (
Context-switching and event-based actors are scheduled cooperatively in a thread pool.
Thread-mapped actors can be used to opt-out of this cooperative scheduling.
%\subsection{Local Actors}
\subsection{Implicit \texttt{self} Pointer}
%The class \lstinline^local_actor^ describes a local running actor.
%It provides a common interface for actor operations like trapping exit messages or finishing execution.
\subsection{The ``Keyword'' \texttt{self}}
The \lstinline^self^ pointer is an essential ingredient of our design.
It identifies the running actor similar to the implicit \lstinline^this^ pointer identifying an object within a member function.
Unlike \lstinline^this^, though, \lstinline^self^ is not limited to a particular scope.
The \lstinline^self^ pointer is used implicitly, whenever an actor calls functions like \lstinline^send^ or \lstinline^become^, but can be accessed to use more advanced actor operations such as linking to another actor, e.g., by calling \lstinline^self->link_to(other)^.
The \lstinline^self^ pointer is convertible to \lstinline^actor_ptr^ and \lstinline^local_actor*^, but it is neither copyable nor assignable.
Thus, \lstinline^auto s = self^ will cause a compiler error, while \lstinline^actor_ptr s = self^ works as expected.
A thread that accesses \lstinline^self^ is converted on-the-fly to an actor if needed.
Hence, ``everything is an actor'' in \libcppa.
However, automatically converted actors use an implementation based on the blocking API, which behaves slightly different than the default, i.e., event-based, implementation.
When using a function or functor to implement an actor, the first argument \emph{can} be used to capture a pointer to the actor itself.
The type of this pointer is \lstinline^event_based_actor^ or \lstinline^blocking_actor^ when using the \lstinline^blocking_api^ flag or their typed counterparts when dealing with typed actors.
Before diving into the API of \libcppa, we would like to take the opportunity to discuss the concepts behind \libcppa and to explain the terminology used in this manual.
\subsection{Actor Model}
The actor model describes concurrent entities -- actors -- that do not share state and communicate only via message passing.
By decoupling concurrently running software components via message passing, the actor model avoids race conditions by design.
Actors can create -- ``spawn'' -- new actors and monitor each other to build fault-tolerant, hierarchical systems.
Since message passing is network transparent, the actor model applies to both concurrency and distribution.
When dealing with dozens of cores, mutexes, semaphores and thread primitives are the wrong level of abstraction.
Implementing applications on top of those primitives has proven challenging and error-prone.
Additionally, mutex-based implementations can cause queueing and unmindful sharing can lead to false sharing -- both decreasing performance significantly, up to the point that an application actually runs when adding more cores.
The actor model has gained momentum over the last decade due to its high level of abstraction and its ability to make efficient use of multicore and multiprocessor machines.
However, the actor model has not yet been widely adopted in the native programming domain.
With \libcppa, we contribute a library for actor programming in C++ as open source software to ease development of concurrent as well as distributed software without sacrificing performance.
\subsection{Terminology}
You will find that \libcppa has not simply adopted exiting implementations based on the actor model such as Erlang or the Akka library.
Instead, \libcppa aims to provide an modern C++ API allowing for type-safe messaging.
\subsubsection{Actor Address}
In \libcppa, each actor has a (network-wide) unique logical address that can be used to identify and monitor it.
However, the address can \emph{not} be used to send a message to an actor.
This limitation is due to the fact that the address does not contain any type information about the actor.
Hence, it would not be safe to send it any message, because the actor might use a strictly typed messaging interface not accepting the given message.
\subsubsection{Actor Handle}
An actor handle contains the address of an actor along with its type information.
In order to send an actor a message, one needs to have a handle to it -- the address alone is not sufficient.
The distinction between handles and addresses -- which is unique to \libcppa when comparing it to other actor systems -- is a consequence of the design decision to support both untyped and typed actors.
\subsubsection{Spawning}
``Spawning'' an actor means to create and run a new actor.
\subsubsection{Monitoring}
\label{sec:monitoring}
A monitored actor sends a ``down message'' to all actors monitoring it as part of its termination.
This allows actors to supervise other actors and to take measures when one of the supervised actors failed, i.e., terminated with a non-normal exit reason.
\subsubsection{Links}
A link is bidirectional connection between two actors.
Each actor sends an ``exit message'' to all of its links as part of its termination.
Unlike down messages (cf. \ref{sec:monitoring}), the default behavior for received exit messages causes the receiving actor to terminate for the same reason if the link has failed, i.e., terminated with a non-normal exit reason.
This allows developers to create a set of actors with the guarantee that either all or no actors are alive.
The default behavior can be overridden, i.e., exit message can be ``trapped''.
When trapping exit messages, they are received as any other ordinary message and can be handled by the actor.
@@ -5,14 +5,14 @@ Messages can be sent by using \lstinline^send^, \lstinline^send_tuple^, or \lsti
\begin{lstlisting}
template<typename... Args>
void send(actor_ptr whom, Args&&... what);
void send(actor whom, Args&&... what);
\end{lstlisting}
The variadic template pack \lstinline^what...^ is converted to a dynamically typed tuple (see Section \ref{Sec::Tuples::DynamicallyTypedTuples}) and then enqueued to the mailbox of \lstinline^whom^.
The following example shows two equal sends, one using \lstinline^send^ and the other using \lstinline^operator<<^.
\begin{lstlisting}
actor_ptr other = spawn(...);
actor other = spawn(...);
send(other, 1, 2, 3);
other << make_any_tuple(1, 2, 3);
\end{lstlisting}
...
...
@@ -21,7 +21,7 @@ Using the function \lstinline^send^ is more compact, but does not have any other
However, note that you should not use \lstinline^send^ if you already have an instance of \lstinline^any_tuple^, because it creates a new tuple containing the old one.
\begin{lstlisting}
actor_ptr other = spawn(...);
actor other = spawn(...);
auto msg = make_any_tuple(1, 2, 3);
send(other, msg); // oops, creates a new tuple that contains msg
send_tuple(other, msg); // ok
...
...
@@ -36,12 +36,11 @@ Choosing one or the other is merely a matter of personal preferences.
\label{Sec::Send::Reply}
The return value of a message handler is used as response message.
During callback invokation, \lstinline^self->last_sender()^ is set.
This identifies the sender of the received message and is used implicitly to reply to the correct sender.
However, using \lstinline^send(self->last_sender(), ...)^ does \emph{not} reply to the message, i.e., synchronous messages will not recognize the message as response.
During callback invocation, \lstinline^self->last_sender()^ is set to the address of the sender.
Actors can also use the result of a \lstinline^send^ to answer to a request, as shown below.
Sending a message to a cooperatively scheduled actor usually causes the receiving actor to be put into the scheduler's job queue if it is currently blocked, i.e., is waiting for a new message.
This job queue is accessed by worker threads.
The \textit{chaining} optimization does not cause the receiver to be put into the scheduler's job queue if it is currently blocked.
The receiver is stored as successor of the currently running actor instead.
Hence, the active worker thread does not need to access the job queue, which significantly speeds up execution.
However, this optimization can be inefficient if an actor first sends a message and then starts computation.
\begin{lstlisting}
void foo(actor_ptr other) {
send(other, ...);
very_long_computation();
// ...
}
int main() {
// ...
auto a = spawn(...);
auto b = spawn(foo, a);
// ...
}
\end{lstlisting}
The example above illustrates an inefficient work flow.
The actor \lstinline^other^ is marked as successor of the \lstinline^foo^ actor but its execution is delayed until \lstinline^very_long_computation()^ is done.
In general, actors should follow the work flow \lstinline^receive^$\Rightarrow$\lstinline^compute^$\Rightarrow$\lstinline^send results^.
However, this optimization can be disabled by calling \lstinline^self->chaining(false)^ if an actor does not match this work flow.
send(other, ...); // not delayed by very_long_compuation
very_long_computation();
// ...
}
\end{lstlisting}
\subsection{Delaying Messages}
Messages can be delayed, e.g., to implement time-based polling strategies, by using one of \lstinline^delayed_send^, \lstinline^delayed_send_tuple^, \lstinline^delayed_reply^, or \lstinline^delayed_reply_tuple^.