@@ -36,16 +37,17 @@ Unlike exit messages, down messages are always treated like any other ordinary m
...
@@ -36,16 +37,17 @@ Unlike exit messages, down messages are always treated like any other ordinary m
An actor will receive one down message for each time it called \lstinline^self->monitor(...)^, even if it adds a monitor to the same actor multiple times.
An actor will receive one down message for each time it called \lstinline^self->monitor(...)^, even if it adds a monitor to the same actor multiple times.
@@ -12,7 +12,8 @@ Thread-mapped actors can be used to opt-out of this cooperative scheduling.
...
@@ -12,7 +12,8 @@ Thread-mapped actors can be used to opt-out of this cooperative scheduling.
\subsection{Implicit \texttt{self} Pointer}
\subsection{Implicit \texttt{self} Pointer}
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.
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.
The type of this pointer is \lstinline^event_based_actor*^ per default and \lstinline^blocking_actor*^ when using the \lstinline^blocking_api^ flag.
When dealing with typed actors, the types are \lstinline^typed_event_based_actor<...>*^ and \lstinline^typed_blocking_actor<...>*^.
\clearpage
\clearpage
\subsection{Interface}
\subsection{Interface}
...
@@ -33,26 +34,20 @@ class local_actor;
...
@@ -33,26 +34,20 @@ class local_actor;
\hline
\hline
\lstinline^bool trap_exit()^& Checks whether this actor traps exit messages \\
\lstinline^bool trap_exit()^& Checks whether this actor traps exit messages \\
\hline
\hline
\lstinline^bool chaining()^& Checks whether this actor uses the ``chained send'' optimization (see Section \ref{Sec::Send::ChainedSend}) \\
\hline
\lstinline^any_tuple last_dequeued()^& Returns the last message that was dequeued from the actor's mailbox\newline\textbf{Note}: Only set during callback invocation \\
\lstinline^any_tuple last_dequeued()^& Returns the last message that was dequeued from the actor's mailbox\newline\textbf{Note}: Only set during callback invocation \\
\hline
\hline
\lstinline^actor_ptr last_sender()^& Returns the sender of the last dequeued message\newline\textbf{Note$_{1}$}: Only set during callback invocation\newline\textbf{Note$_{2}$}: Used implicitly to send response messages (see Section \ref{Sec::Send::Reply})\\
\lstinline^actor_addr last_sender()^& Returns the sender of the last dequeued message\newline\textbf{Note}: Only set during callback invocation\\
\hline
\hline
\lstinline^vector<group_ptr> joined_groups()^& Returns all subscribed groups \\
\lstinline^vector<group> joined_groups()^& Returns all subscribed groups \\
\lstinline^void trap_exit(bool enabled)^& Enables or disables trapping of exit messages \\
\lstinline^void trap_exit(bool enabled)^& Enables or disables trapping of exit messages \\
\hline
\hline
\lstinline^void chaining(bool enabled)^& Enables or disables chained send \\
\lstinline^void join(const group& g)^& Subscribes to group \lstinline^g^\\
\hline
\lstinline^void join(const group_ptr& g)^& Subscribes to group \lstinline^g^\\
\hline
\hline
\lstinline^void leave(const group_ptr& g)^& Unsubscribes group \lstinline^g^\\
\lstinline^void leave(const group& g)^& Unsubscribes group \lstinline^g^\\
\hline
\lstinline^auto make_response_handle()^& Creates a handle that can be used to respond to the last received message later on, e.g., after receiving another message \\
\hline
\hline
\lstinline^void on_sync_failure(auto fun)^& Sets a handler, i.e., a functor taking no arguments, for unexpected synchronous response messages (default action is to kill the actor for reason \lstinline^unhandled_sync_failure^) \\
\lstinline^void on_sync_failure(auto fun)^& Sets a handler, i.e., a functor taking no arguments, for unexpected synchronous response messages (default action is to kill the actor for reason \lstinline^unhandled_sync_failure^) \\
\hline
\hline
...
@@ -62,8 +57,6 @@ class local_actor;
...
@@ -62,8 +57,6 @@ class local_actor;
\hline
\hline
\lstinline^void demonitor(actor_ptr whom)^& Removes a monitor from \lstinline^whom^\\
\lstinline^void demonitor(actor_ptr whom)^& Removes a monitor from \lstinline^whom^\\
\hline
\hline
\lstinline^void exec_behavior_stack()^& Executes an actor's behavior stack until it is empty \\
\hline
\lstinline^bool has_sync_failure_handler()^& Checks wheter this actor has a user-defined sync failure handler \\
\lstinline^bool has_sync_failure_handler()^& Checks wheter this actor has a user-defined sync failure handler \\
\subsection{Using \texttt{aout} -- A Concurrency-safe Wrapper for \texttt{cout}}
\subsection{Using \texttt{aout} -- A Concurrency-safe Wrapper for \texttt{cout}}
When using \lstinline^cout^ from multiple actors, output often appears interleaved.
When using \lstinline^cout^ from multiple actors, output often appears interleaved.
Moreover, using \lstinline^cout^ from multiple actors -- and thus multiple threads -- in parallel should be avoided, since the standard does not guarantee a thread-safe implementation.
Moreover, using \lstinline^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{cppa::aout}, actors can achieve a concurrency-safe text output.
By replacing \texttt{std::cout} with \texttt{cppa::aout}, actors can achieve a concurrency-safe text output.
The header \lstinline^cppa/cppa.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).
The header \lstinline^cppa/cppa.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 (keep in mind, sending messages from actor constructors is not safe).
Each write operation to \texttt{aout} sends a message to a `hidden' actor (keep in mind, sending messages from actor constructors is not safe).
This actor only prints lines, unless output is forced using \lstinline^flush^.
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).
\begin{lstlisting}
\begin{lstlisting}
#include <chrono>
#include <chrono>
#include <cstdlib>
#include <cstdlib>
#include <iostream>
#include "cppa/cppa.hpp"
#include "cppa/cppa.hpp"
using namespace cppa;
using namespace cppa;
...
@@ -72,17 +74,19 @@ using std::endl;
...
@@ -72,17 +74,19 @@ using std::endl;
int main() {
int main() {
std::srand(std::time(0));
std::srand(std::time(0));
for (int i = 1; i <= 50; ++i) {
for (int i = 1; i <= 50; ++i) {
spawn([i] {
spawn<blocking_api>([i](blocking_actor* self) {
aout << "Hi there! This is actor nr. " << i << "!" << endl;
\item The functions \lstinline^become^ and \lstinline^handle_response^ do not block, i.e., always return immediately.
\item The functions \lstinline^become^ and \lstinline^handle_response^ do not block, i.e., always return immediately.
Thus, you should \textit{always} capture by value in event-based actors, because all references on the stack will cause undefined behavior if a lambda is executed.
Thus, one should \textit{always} capture by value in lambda expressions, because all references on the stack will cause undefined behavior if the lambda expression is executed.
\end{itemize}
\subsection{Mixing Event-Based and Blocking API}
\begin{itemize}
\item Blocking \libcppa function such as \lstinline^receive^ will \emph{throw an exception} if accessed from an event-based actor.
%To catch as many errors as possible at compile-time, \libcppa will produce an error if \lstinline^receive^ is called and the \lstinline^this^ pointer is set and points to an event-based actor.
\item Context-switching and thread-mapped actors \emph{can} use the \lstinline^become^ API.
Whenever a non-event-based actor calls \lstinline^become()^ for the first time, it will create a behavior stack and execute it until the behavior stack is empty.
Thus, the \textit{initial}\lstinline^become^ blocks until the behavior stack is empty, whereas all subsequent calls to \lstinline^become^ will return immediately.
Related functions, e.g., \lstinline^sync_send(...).then(...)^, behave the same, as they manipulate the behavior stack as well.
\end{itemize}
\end{itemize}
\subsection{Synchronous Messages}
\subsection{Synchronous Messages}
\begin{itemize}
\begin{itemize}
\item
\lstinline^send(self->last_sender(), ...)^ does \textbf{not} send a response message.
\item
\item
A handle returned by \lstinline^sync_send^ represents \emph{exactly one} response message.
A handle returned by \lstinline^sync_send^ represents \emph{exactly one} response message.
Therefore, it is not possible to receive more than one response message.
Therefore, it is not possible to receive more than one response message.
%Calling \lstinline^reply^ more than once will result in lost messages and calling \lstinline^handle_response^ or \lstinline^receive_response^ more than once on a future will throw an exception.
%Calling \lstinline^reply^ more than once will result in lost messages and calling \lstinline^handle_response^ or \lstinline^receive_response^ more than once on a future will throw an exception.
\item
\item
The future returned by \lstinline^sync_send^ is bound to the calling actor.
The handle returned by \lstinline^sync_send^ is bound to the calling actor.
It is not possible to transfer such a future to another actor.
It is not possible to transfer a handle to a response to another actor.
Calling \lstinline^receive_response^ or \lstinline^handle_response^ for a future bound to another actor is undefined behavior.
\end{itemize}
\end{itemize}
...
@@ -44,29 +28,27 @@ Calling \lstinline^receive_response^ or \lstinline^handle_response^ for a future
...
@@ -44,29 +28,27 @@ Calling \lstinline^receive_response^ or \lstinline^handle_response^ for a future
\begin{itemize}
\begin{itemize}
\item
\item
\lstinline^send(whom, ...)^ is syntactic sugar for \lstinline^whom << make_any_tuple(...)^.
\lstinline^send(whom, ...)^ is defined as \lstinline^send_tuple(whom, make_any_tuple(...))^.
Hence, a message sent via \lstinline^send(whom, self->last_dequeued())^ will not yield the expected result, since it wraps \lstinline^self->last_dequeued()^ into another \lstinline^any_tuple^ instance.
Hence, a message sent via \lstinline^send(whom, self->last_dequeued())^ will not yield the expected result, since it wraps \lstinline^self->last_dequeued()^ into another \lstinline^any_tuple^ instance.
The correct way of forwarding messages is \lstinline^self->forward_to(whom)^.
The correct way of forwarding messages is \lstinline^self->forward_to(whom)^.
\end{itemize}
\end{itemize}
\clearpage
\subsection{Sharing}
\subsection{Sharing}
\begin{itemize}
\begin{itemize}
\item It is strongly recommended to \textbf{not} share states between actors.
\item It is strongly recommended to \textbf{not} share states between actors.
In particular, no actor shall ever access member variables or member functions of another actor.
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.
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} and all actors access the data only via smart pointers such as \lstinline^std::shared_ptr^.
However, sharing \textit{data} between actors is fine, as long as the data is \textit{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.
Nevertheless, the recommended way of sharing informations is message passing.
Sending data to multiple actors does \textit{not} result in copying the data several times.
Sending data to multiple actors does not necessarily result in copying the data several times.
Read Section \ref{Sec::Tuples} to learn more about \libcppa's copy-on-write optimization for tuples.
Read Section \ref{Sec::Tuples} to learn more about \libcppa's copy-on-write optimization for tuples.
\end{itemize}
\end{itemize}
\subsection{Constructors of Class-based Actors}
\subsection{Constructors of Class-based Actors}
\begin{itemize}
\begin{itemize}
\item During constructor invocation, \lstinline^self^ does \textbf{not} point to \lstinline^this^. It points to the invoking actor instead.
\item You should \textbf{not} try to send or receive messages in a constructor or destructor, because the actor is not fully initialized at this point.
\item You should \textbf{not} send or receive messages in a constructor or destructor.
@@ -9,18 +9,21 @@ By decoupling concurrently running software components via message passing, the
...
@@ -9,18 +9,21 @@ By decoupling concurrently running software components via message passing, the
Actors can create -- ``spawn'' -- new actors and monitor each other to build fault-tolerant, hierarchical systems.
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.
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.
When dealing with dozens of cores, mutexes, semaphores and other threading primitives are the wrong level of abstraction.
Implementing applications on top of those primitives has proven challenging and error-prone.
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.
Additionally, mutex-based implementations can cause queueing and unmindful access to (even distinct) data from separate threads in parallel can lead to false sharing -- both decreasing performance significantly, up to the point that an application actually runs slower 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.
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.
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.
With \libcppa, 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, \libcppa follows the C++ philosophy ``building the highest abstraction possible without sacrificing performance''.
\subsection{Terminology}
\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.
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.
Instead, \libcppa aims to provide a modern C++ API allowing for type-safe as well as dynamically typed messaging.
Hence, most aspects of our system are familiar to developers having experience with other actor systems, but there are also slight differences in terminology.
However, neither \libcppa nor this manual require any foreknowledge.
\subsubsection{Actor Address}
\subsubsection{Actor Address}
...
@@ -35,6 +38,18 @@ An actor handle contains the address of an actor along with its type information
...
@@ -35,6 +38,18 @@ 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.
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.
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{Untyped Actors}
An untyped actor does not constrain the type of messages it receives, i.e., a handle to an untyped actor accepts any kind of message.
That does of course not mean that untyped actors must handle all possible types of messages.
Choosing typed vs untyped actors is mostly a matter of taste.
Untyped actors allow developers to build prototypes faster, while typed actors allow the compiler to fetch more errors at compile time.
\subsubsection{Typed Actor}
A typed actor defines its messaging interface, i.e., both input and output types, in its type.
This allows the compiler to check message types statically.
\subsubsection{Spawning}
\subsubsection{Spawning}
``Spawning'' an actor means to create and run a new actor.
``Spawning'' an actor means to create and run a new actor.
...
@@ -45,7 +60,7 @@ The distinction between handles and addresses -- which is unique to \libcppa whe
...
@@ -45,7 +60,7 @@ The distinction between handles and addresses -- which is unique to \libcppa whe
A monitored actor sends a ``down message'' to all actors monitoring it as part of its termination.
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.
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}
\subsubsection{Link}
A link is bidirectional connection between two actors.
A link is bidirectional connection between two actors.
Each actor sends an ``exit message'' to all of its links as part of its termination.
Each actor sends an ``exit message'' to all of its links as part of its termination.
@@ -73,6 +73,7 @@ Note that the second version does call \lstinline^on^ without template parameter
...
@@ -73,6 +73,7 @@ Note that the second version does call \lstinline^on^ without template parameter
Furthermore, \lstinline^arg_match^ must be passed as last parameter.
Furthermore, \lstinline^arg_match^ must be passed as last parameter.
If all types should be deduced from the callback signature, \lstinline^on_arg_match^ can be used.
If all types should be deduced from the callback signature, \lstinline^on_arg_match^ can be used.
It is equal to \lstinline^on(arg_match)^.
It is equal to \lstinline^on(arg_match)^.
However, when using a pattern to initialize the behavior of an actor, \lstinline^on_arg_match^ is used implicitly whenever a functor is passed without preceding it with an \lstinline^on^ clause.
@@ -8,15 +8,14 @@ The given behavior is then executed until it is replaced by another call to \lst
...
@@ -8,15 +8,14 @@ The given behavior is then executed until it is replaced by another call to \lst
\subsection{Class-based actors}
\subsection{Class-based actors}
A class-based actor is a subtype of \lstinline^event_based_actor^ and must implement the pure virtual member function \lstinline^init^.
A class-based actor is a subtype of \lstinline^event_based_actor^ and must implement the pure virtual member function \lstinline^make_behavior^ returning the initial behavior.
An implementation of \lstinline^init^ shall set an initial behavior by using \lstinline^become^.
\begin{lstlisting}
\begin{lstlisting}
class printer : public event_based_actor {
class printer : public event_based_actor {
behavior make_behavior() override {
behavior make_behavior() override {
return {
return {
others() >> [] {
others() >> [] {
cout << to_string(self->last_received()) << endl;
cout << to_string(last_received()) << endl;
}
}
};
};
}
}
...
@@ -28,11 +27,11 @@ This base class simply returns \lstinline^init_state^ (defined in the subclass)
...
@@ -28,11 +27,11 @@ This base class simply returns \lstinline^init_state^ (defined in the subclass)
\begin{lstlisting}
\begin{lstlisting}
struct printer : sb_actor<printer> {
struct printer : sb_actor<printer> {
behavior init_state = (
behavior init_state {
others() >> [] {
others() >> [] {
cout << to_string(self->last_received()) << endl;
cout << to_string(self->last_received()) << endl;
}
}
);
};
};
};
\end{lstlisting}
\end{lstlisting}
...
@@ -46,7 +45,7 @@ class fixed_stack : public sb_actor<fixed_stack> {
...
@@ -46,7 +45,7 @@ class fixed_stack : public sb_actor<fixed_stack> {
friend class sb_actor<fixed_stack>;
friend class sb_actor<fixed_stack>;
size_t max_size = 10;
size_t max_size;
vector<int> data;
vector<int> data;
...
@@ -103,22 +102,22 @@ An actor can set a new behavior by calling \lstinline^become^ with the \lstinlin
...
@@ -103,22 +102,22 @@ An actor can set a new behavior by calling \lstinline^become^ with the \lstinlin
\begin{lstlisting}
\begin{lstlisting}
// receives {int, float} sequences
// receives {int, float} sequences
void testee() {
behavior testee(event_based_actor* self) {
become (
return {
on_arg_match >> [=](int value1) {
[=](int value1) {
become (
self->become (
// the keep_behavior policy stores the current behavior
// the keep_behavior policy stores the current behavior
// on the behavior stack to be able to return to this
// on the behavior stack to be able to return to this
// behavior later on by calling unbecome()
// behavior later on by calling unbecome()
keep_behavior,
keep_behavior,
on_arg_match >> [=](float value2) {
[=](float value2) {
cout << value1 << " => " << value2 << endl;
cout << value1 << " => " << value2 << endl;
// restore previous behavior
// restore previous behavior
unbecome();
self->unbecome();
}
}
);
);
}
}
);
};
}
}
\end{lstlisting}
\end{lstlisting}
...
@@ -146,16 +145,16 @@ But often, we need to be able to recover if an expected messages does not arrive
...
@@ -146,16 +145,16 @@ But often, we need to be able to recover if an expected messages does not arrive
using std::endl;
using std::endl;
void eager_actor() {
behavior eager_actor(event_based_actor* self) {
become (
return {
on_arg_match >> [](int i) { /* ... */ },
[](int i) { /* ... */ },
on_arg_match >> [](float i) { /* ... */ },
[](float i) { /* ... */ },
others() >> [] { /* ... */ },
others() >> [] { /* ... */ },
after(std::chrono::seconds(10)) >> [](){
after(std::chrono::seconds(10)) >> [] {
aout << "received nothing within 10 seconds..." << endl;
aout(self) << "received nothing within 10 seconds..." << endl;
// ...
// ...
}
}
);
};
}
}
\end{lstlisting}
\end{lstlisting}
...
@@ -181,18 +180,18 @@ Afterwards, the server returns to its initial behavior, i.e., awaits the next \l
...
@@ -181,18 +180,18 @@ Afterwards, the server returns to its initial behavior, i.e., awaits the next \l
The server actor will exit for reason \lstinline^user_defined^ whenever it receives a message that is neither a request, nor an idle message.
The server actor will exit for reason \lstinline^user_defined^ whenever it receives a message that is neither a request, nor an idle message.
\begin{lstlisting}
\begin{lstlisting}
void server() {
behavior server(event_based_actor* self) {
auto die = [=] { self->quit(exit_reason::user_defined); };
auto die = [=] { self->quit(exit_reason::user_defined); };
Messages can be sent by using \lstinline^send^, \lstinline^send_tuple^, or \lstinline^operator<<^. The variadic template function \lstinline^send^ has the following signature.
Messages can be sent by using the member function \lstinline^send^ or \lstinline^send_tuple^.
The variadic template function \lstinline^send^ has the following signature.
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 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 other = spawn(...);
send(other, 1, 2, 3);
other << make_any_tuple(1, 2, 3);
\end{lstlisting}
Using the function \lstinline^send^ is more compact, but does not have any other benefit.
Using the function \lstinline^send^ is more compact, but does not have any other benefit.
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.
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}
\begin{lstlisting}
actor other = spawn(...);
void some_fun(event_based_actor* self) {
auto msg = make_any_tuple(1, 2, 3);
actor other = spawn(...);
send(other, msg); // oops, creates a new tuple that contains msg
auto msg = make_any_tuple(1, 2, 3);
send_tuple(other, msg); // ok
self->send(other, msg); // oops, creates a new tuple containing msg
other << msg; // ok
self->send_tuple(other, msg); // ok
}
\end{lstlisting}
\end{lstlisting}
The function \lstinline^send_tuple^ is equal to \lstinline^operator<<^.
Choosing one or the other is merely a matter of personal preferences.
\clearpage
\clearpage
\subsection{Replying to Messages}
\subsection{Replying to Messages}
\label{Sec::Send::Reply}
\label{Sec::Send::Reply}
The return value of a message handler is used as response message.
The return value of a message handler is used as response message.
During callback invocation, \lstinline^self->last_sender()^ is set to the address of the sender.
Actors can also use the result of a \lstinline^sync_send^ to answer to a request, as shown below.
Actors can also use the result of a \lstinline^send^ to answer to a request, as shown below.
\subsection{Forwarding Messages in Untyped Actors}
The function \lstinline^forward_to^ forwards the last dequeued message to an other actor.
The member function \lstinline^forward_to^ forwards the last dequeued message to an other actor.
Forwarding a synchronous message will also transfer responsibility for the request, i.e., the receiver of the forwarded message can reply as usual and the original sender of the message will receive the response.
Forwarding a synchronous message will also transfer responsibility for the request, i.e., the receiver of the forwarded message can reply as usual and the original sender of the message will receive the response.
The following diagram illustrates forwarding of a synchronous message from actor \texttt{B} to actor \texttt{C}.
The following diagram illustrates forwarding of a synchronous message from actor \texttt{B} to actor \texttt{C}.
@@ -5,9 +5,9 @@ Actors are created using the function \lstinline^spawn^.
...
@@ -5,9 +5,9 @@ Actors are created using the function \lstinline^spawn^.
%\subsection{Using \lstinline^spawn^ to Create Actors from Functors or Classes}
%\subsection{Using \lstinline^spawn^ to Create Actors from Functors or Classes}
The easiest way to implement actors is to use functors, e.g., a free function or lambda expression.
The easiest way to implement actors is to use functors, e.g., a free function or lambda expression.
The arguments to the functor are passed to \lstinline^spawn^ as additional arguments.
The arguments to the functor are passed to \lstinline^spawn^ as additional arguments.
The function \lstinline^spawn^ also takes optional flags as template paremeter.
The function \lstinline^spawn^ also takes optional flags as template parameter.
%The optional template parameter of \lstinline^spawn^ decides whether an actor should run in its own thread or takes place in the cooperative scheduling.
%The optional template parameter of \lstinline^spawn^ decides whether an actor should run in its own thread or takes place in the cooperative scheduling.
The flag \lstinline^detached^ causes \lstinline^spawn^ to create a thread-mapped actor (opt-out of the cooperative scheduling), the flag \lstinline^linked^ links the newly created actor to its parent, and the flag \lstinline^monitored^ automatically adds a monitor to the new actor.
The flag \lstinline^detached^ causes \lstinline^spawn^ to create a thread-mapped actor (opt-out of the cooperative scheduling), the flag \lstinline^linked^ links the newly created actor to its parent -- not available on top-level spawn -- and the flag \lstinline^monitored^ automatically adds a monitor to the new actor.
Actors that make use of the blocking API (see Section \ref{Sec::BlockingAPI}) must be spawned using the flag \lstinline^blocking_api^.
Actors that make use of the blocking API (see Section \ref{Sec::BlockingAPI}) must be spawned using the flag \lstinline^blocking_api^.
Flags are concatenated using the operator \lstinline^+^, as shown in the examples below.
Flags are concatenated using the operator \lstinline^+^, as shown in the examples below.
...
@@ -17,7 +17,7 @@ Flags are concatenated using the operator \lstinline^+^, as shown in the example
...
@@ -17,7 +17,7 @@ Flags are concatenated using the operator \lstinline^+^, as shown in the example
// spawn actors that need access to the blocking API
// spawn actors that need access to the blocking API
auto b0 = spawn<blocking_api>(ugly_duckling);
auto aa = self->spawn<blocking_api>(ugly_duckling);
// compiler error: my_actor2 captures the implicit
// self pointer as event_based_actor* and thus cannot
// be spawned using blocking_api flag
/*-auto ab = self->spawn<blocking_api>(my_actor2);-*/
}
}
\end{lstlisting}
\end{lstlisting}
\textbf{Note}: \lstinline^spawn(fun, arg0, ...)^ is \textbf{not} equal to \lstinline^spawn(std::bind(fun, arg0, ...))^!
%TODO: check how std::bind(..., self) behaves atm
For example, a call to \lstinline^spawn(fun, self, ...)^ will pass a pointer to the calling actor to the newly created actor, as expected, whereas \lstinline^spawn(std::bind(fun, self, ...))^ wraps the type of \lstinline^self^ into the function wrapper and evaluates \lstinline^self^ on function invocation.
%\textbf{Note}: \lstinline^spawn(fun, arg0, ...)^ is \textbf{not} equal to \lstinline^spawn(std::bind(fun, arg0, ...))^!
Thus, the actor will end up having a pointer \emph{to itself} rather than a pointer to its parent.
%For example, a call to \lstinline^spawn(fun, self, ...)^ will pass a pointer to the calling actor to the newly created actor, as expected, whereas \lstinline^spawn(std::bind(fun, self, ...))^ wraps the type of \lstinline^self^ into the function wrapper and evaluates \lstinline^self^ on function invocation.
%Thus, the actor will end up having a pointer \emph{to itself} rather than a pointer to its parent.
Strongly typed actors provide a convenient way of defining type-safe messaging interfaces.
Strongly typed actors provide a convenient way of defining type-safe messaging interfaces.
Unlike ``dynamic actors'', typed actors are not allowed to change their behavior at runtime, neither are typed actors allowed to use guard expressions.
Unlike untyped actorsd, typed actors are not allowed to use guard expressions.
When calling \lstinline^become^ in a strongly typed actor, the actor will be killed with exit reason \lstinline^unallowed_function_call^.
When calling \lstinline^become^ in a strongly typed actor, \emph{all} message handlers from the typed interface must be set.
Typed actors use \lstinline^typed_actor_ptr<...>^ instead of \lstinline^actor_ptr^, whereas the template parameters hold the messaging interface.
For example, an actor responding to two integers with a dobule would use the type \lstinline^typed_actor_ptr<replies_to<int, int>::with<double>>^.
Typed actors use handles of type \lstinline^typed_actor<...>^ rather than \lstinline^actor^, whereas the template parameters hold the messaging interface.
For example, an actor responding to two integers with a dobule would use the type \lstinline^typed_actor<replies_to<int, int>::with<double>>^.
All functions for message passing, linking and monitoring are overloaded to accept both types of actors.
All functions for message passing, linking and monitoring are overloaded to accept both types of actors.
As of version 0.8, strongly typed actors cannot be published and do not support message priorities (those are planned feature for future releases).
\subsection{Spawning Typed Actors}
\subsection{Spawning Typed Actors}
\label{sec:strong:spawn}
\label{sec:strong:spawn}
Actors are spawned using the function \lstinline^spawn_typed^.
Typed actors are spawned using the function \lstinline^spawn_typed^.
The argument to this function call \emph{must} be a match expression as shown in the example below, because the runtime of libcppa needs to evaluate the signature of each message handler.
The argument to this function call \emph{must} be a match expression as shown in the example below, because the runtime of libcppa needs to evaluate the signature of each message handler.
\begin{lstlisting}
\begin{lstlisting}
...
@@ -46,32 +44,94 @@ subtype2 p3 = p0;
...
@@ -46,32 +44,94 @@ subtype2 p3 = p0;
\clearpage
\clearpage
\subsection{Class-based Typed Actors}
\subsection{Class-based Typed Actors}
Typed actors can be implemented using a class by inheriting from \lstinline^typed_actor<...>^, whereas the template parameter pack denotes the messaging interface.
Typed actors are spawned using the function \lstinline^spawn_typed^ and define their message passing interface as list of \lstinline^replies_to<...>::with<...>^ statements.
Derived classes have to implemented the pure virtual member function \lstinline^make_behavior^, as shown in the example below.
This interface is used in (1) \lstinline^typed_event_based_actor<...>^, which is the base class for typed actors, (2) the handle type \lstinline^typed_actor<...>^, and (3) \lstinline^typed_behavior<...>^, i.e., the behavior definition for typed actors.
Since this is rather redundant, the actor handle provides definitions for the behavior as well as the base class, as shown in the example below.
It is worth mentioning that all typed actors always use the event-based implementation, i.e., there is no typed actor implementation providing a blocking API.
\begin{lstlisting}
\begin{lstlisting}
// implementation
struct shutdown_request {};
class typed_testee : public typed_actor<replies_to<int>::with<bool>> {
\libcppa uses a future-based API for synchronous communication.
\libcppa supports both asynchronous and synchronous communication.
The functions \lstinline^sync_send^ and \lstinline^sync_send_tuple^ send synchronous request messages to the receiver and return a future to the response message.
The member functions \lstinline^sync_send^ and \lstinline^sync_send_tuple^ send synchronous request messages.
Note that the returned future is \textit{actor-local}, i.e., only the actor that has send the corresponding request message is able to receive the response identified by such a future.
A synchronous message is sent to the receiving actor's mailbox like any other asynchronous message.
A synchronous message is sent to the receiving actor's mailbox like any other asynchronous message.
...
@@ -27,42 +26,39 @@ The response message, on the other hand, is treated separately.
...
@@ -27,42 +26,39 @@ The response message, on the other hand, is treated separately.
The difference between \lstinline^sync_send^ and \lstinline^timed_sync_send^ is how timeouts are handled.
The difference between \lstinline^sync_send^ and \lstinline^timed_sync_send^ is how timeouts are handled.
The behavior of \lstinline^sync_send^ is analogous to \lstinline^send^, i.e., timeouts are specified by using \lstinline^after(...)^ statements (see \ref{Sec::Receive::Timeouts}).
The behavior of \lstinline^sync_send^ is analogous to \lstinline^send^, i.e., timeouts are specified by using \lstinline^after(...)^ statements (see \ref{Sec::Receive::Timeouts}).
When using \lstinline^timed_sync_send^ function, \lstinline^after(...)^ statements are ignored and the actor will receive a \lstinline^'TIMEOUT'^ message after the given duration instead.
When using \lstinline^timed_sync_send^ function, \lstinline^after(...)^ statements are ignored and the actor will receive a \lstinline^sync_timeout_msg^ after the given duration instead.
\subsection{Error Messages}
\subsection{Error Messages}
When using synchronous messaging, \libcppa's runtime environment will send ...
When using synchronous messaging, \libcppa's runtime environment will send ...
\begin{itemize}
\begin{itemize}
\item\lstinline^{'EXITED', uint32_t exit_reason}^ if the receiver is not alive
\itemif the receiver is not alive:\newline\lstinline^sync_exited_msg { actor_addr source; std::uint32_t reason; };^
\item\lstinline^{'VOID'}^ if the receiver handled the message but did not respond to it
%\item \lstinline^{'VOID'}^ if the receiver handled the message but did not respond to it
\item\lstinline^{'TIMEOUT'}^ if a message send by \lstinline^timed_sync_send^ timed out
\itemif a message send by \lstinline^timed_sync_send^ timed out: \lstinline^sync_timeout_msg^
\end{itemize}
\end{itemize}
\clearpage
\clearpage
\subsection{Receive Response Messages}
\subsection{Receive Response Messages}
The function \lstinline^handle_response^ can be used to set a one-shot handler receiving the response message send by \lstinline^sync_send^.
When sending a synchronous message, the response handler can be passed by either using \lstinline^then^ (event-based actors) or \lstinline^await^ (blocking actors).
\begin{lstlisting}
\begin{lstlisting}
actor_ptr testee = ...; // replies with a string to 'get'
void foo(event_based_actor* self, actor testee) {
// testee replies with a string to 'get'
// "handle_response" usage example
self->sync_send(testee, atom("get")).then(
auto handle = sync_send(testee, atom("get"));
on_arg_match >> [=](const std::string& str) {
handle_response (handle) (
// handle str
on_arg_match >> [=](const std::string& str) {
},
// handle str
after(std::chrono::seconds(30)) >> [=]() {
},
// handle error
after(std::chrono::seconds(30)) >> [=]() {
}
// handle error
);
}
);
);
\end{lstlisting}
\end{lstlisting}
Similar to \lstinline^become^, the function \lstinline^handle_response^ modifies an actor's behavior stack.
Similar to \lstinline^become^, the \lstinline^then^ function modifies an actor's behavior stack.
However, it is used as ``one-shot handler'' and automatically returns the previous actor behavior afterwards.
However, it is used as ``one-shot handler'' and automatically returns to the previous behavior afterwards.
It is possible to ``stack'' multiple \lstinline^handle_response^ calls.
Each response handler is executed once and then automatically discarded.
\subsection{Synchronous Failures and Error Handlers}
\subsection{Synchronous Failures and Error Handlers}
...
@@ -70,72 +66,43 @@ An unexpected response message, i.e., a message that is not handled by given beh
...
@@ -70,72 +66,43 @@ An unexpected response message, i.e., a message that is not handled by given beh
The default handler kills the actor by calling \lstinline^self->quit(exit_reason::unhandled_sync_failure)^.
The default handler kills the actor by calling \lstinline^self->quit(exit_reason::unhandled_sync_failure)^.
The handler can be overridden by calling \lstinline^self->on_sync_failure(/*...*/)^.
The handler can be overridden by calling \lstinline^self->on_sync_failure(/*...*/)^.
Unhandled \lstinline^'TIMEOUT'^ messages trigger the \lstinline^on_sync_timeout^ handler.
Unhandled timeout messages trigger the \lstinline^on_sync_timeout^ handler.
The default handler kills the actor for reason \lstinline^exit_reason::unhandled_sync_failure^.
The default handler kills the actor for reason \lstinline^exit_reason::unhandled_sync_failure^.
It is possible set both error handlers by calling \lstinline^self->on_sync_timeout_or_failure(/*...*)^.
It is possible set both error handlers by calling \lstinline^self->on_sync_timeout_or_failure(/*...*)^.
\clearpage
\subsection{Using \lstinline^then^ to Receive a Response}
Often, an actor sends a synchronous message and then wants to wait for the response.
In this case, using either \lstinline^handle_response^ is quite verbose.
To allow for a more compact code, \lstinline^message_future^ provides the member function \lstinline^then^.
Using this member function is equal to using \lstinline^handle_response^, as illustrated by the following example.
\begin{lstlisting}
\begin{lstlisting}
actor_ptr testee = ...; // replies with a string to 'get'
\subsubsection{Continuations for Event-based Actors}
\subsubsection{Continuations for Event-based Actors}
\libcppa supports continuations to enable chaining of send/receive statements.
\libcppa supports continuations to enable chaining of send/receive statements.
The functions \lstinline^handle_response^ and \lstinline^message_future::then^ both return a helper object offering the member function \lstinline^continue_with^, which takes a functor $f$ without arguments.
The functions \lstinline^then^ returns a helper object offering the member function \lstinline^continue_with^, which takes a functor $f$ without arguments.
After receiving a message, $f$ is invoked if and only if the received messages was handled successfully, i.e., neither \lstinline^sync_failure^ nor \lstinline^sync_timeout^ occurred.
After receiving a message, $f$ is invoked if and only if the received messages was handled successfully, i.e., neither \lstinline^sync_failure^ nor \lstinline^sync_timeout^ occurred.
\begin{lstlisting}
\begin{lstlisting}
actor_ptr d_or_s = ...; // replies with either a double or a string
void foo(event_based_actor* self) {
actor d_or_s = ...; // replies with either a double or a string