\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^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^message 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_addr last_sender()^& Returns the sender of the last dequeued message\newline\textbf{Note}: Only set during callback invocation \\
\lstinline^actor_addr last_sender()^& Returns the sender of the last dequeued message\newline\textbf{Note}: Only set during callback invocation \\
@@ -28,8 +28,8 @@ It is not possible to transfer a handle to a response to another actor.
...
@@ -28,8 +28,8 @@ It is not possible to transfer a handle to a response to another actor.
\begin{itemize}
\begin{itemize}
\item
\item
\lstinline^send(whom, ...)^ is defined as \lstinline^send_tuple(whom, make_any_tuple(...))^.
\lstinline^send(whom, ...)^ is defined as \lstinline^send_tuple(whom, make_message(...))^.
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^message^ 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)^.
The message passing implementation of \lib uses tuples with call-by-value semantic.
Hence, it is not necessary to declare message types, though, \lib allows users to use user-defined types in messages (see Section \ref{Sec::TypeSystem::UserDefined}).
A call-by-value semantic would cause multiple copies of a tuple if it is send to multiple actors.
To avoid unnecessary copying overhead, \lib uses a copy-on-write tuple implementation.
A tuple is implicitly shared between any number of actors, as long as all actors demand only read access.
Whenever an actor demands write access, it has to copy the data first if more than one reference to it exists.
Thus, race conditions cannot occur and each tuple is copied only if necessary.
The interface of \lstinline^cow_tuple^ strictly distinguishes between const and non-const access.
The template function \lstinline^get^ returns an element as immutable value, while \lstinline^get_ref^ explicitly returns a mutable reference to the required value and detaches the tuple if needed.
We do not provide a const overload for \lstinline^get^, because this would cause to unintended, and thus unnecessary, copying overhead.
\begin{lstlisting}
auto x1 = make_cow_tuple(1, 2, 3); // cow_tuple<int, int, int>
auto x2 = x1; // cow_tuple<int, int, int>
assert(&get<0>(x1) == &get<0>(x2)); // point to the same data
get_ref<0>(x1) = 10; // detaches x1 from x2
//get<0>(x1) = 10; // compiler error
assert(get<0>(x1) == 10); // x1 is now {10, 2, 3}
assert(get<0>(x2) == 1); // x2 is still {1, 2, 3}
assert(&get<0>(x1) != &get<0>(x2)); // no longer the same
\end{lstlisting}
\subsection{Dynamically Typed Tuples}
\label{Sec::Tuples::DynamicallyTypedTuples}
The class \lstinline^any_tuple^ represents a tuple without static type information.
All messages send between actors use this tuple type.
The type information can be either explicitly accessed for each element or the original tuple, or a subtuple of it, can be restored using \lstinline^tuple_cast^.
Users of \lib usually do not need to know about
\lstinline^any_tuple^, since it is used ``behind the scenes''.
However, \lstinline^any_tuple^ can be created from a \lstinline^cow_tuple^
or by using \lstinline^make_any_tuple^, as shown below.
\begin{lstlisting}
auto x1 = make_cow_tuple(1, 2, 3); // cow_tuple<int, int, int>
The function \lstinline^tuple_cast^ restores static type information
from an \lstinline^any_tuple^ object.
It returns an \lstinline^option^ (see Section \ref{Appendix::Option})
for a \lstinline^cow_tuple^ of the requested types.
\begin{lstlisting}
auto x1 = make_any_tuple(1, 2, 3);
auto x2_opt = tuple_cast<int, int, int>(x1);
assert(x2_opt.valid());
auto x2 = *x2_opt;
assert(get<0>(x2) == 1);
assert(get<1>(x2) == 2);
assert(get<2>(x2) == 3);
\end{lstlisting}
The function \lstinline^tuple_cast^ can be used with wildcards (see Section \ref{Sec::PatternMatching::Wildcards}) to create a view to a subset of the original data.
No elements are copied, unless the tuple becomes detached.
@@ -4,14 +4,14 @@ Before diving into the API of \lib, we would like to take the opportunity to dis
...
@@ -4,14 +4,14 @@ Before diving into the API of \lib, we would like to take the opportunity to dis
\subsection{Actor Model}
\subsection{Actor Model}
The actor model describes concurrent entities -- actors -- that do not share state and communicate only via message passing.
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.
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.
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 other threading 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 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.
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.
@@ -7,6 +7,8 @@ A broker is an event-based actor running in the middleman that multiplexes socke
...
@@ -7,6 +7,8 @@ A broker is an event-based actor running in the middleman that multiplexes socke
It can maintain any number of acceptors and connections.
It can maintain any number of acceptors and connections.
Since the broker runs in the middleman, implementations should be careful to consume as little time as possible in message handlers.
Since the broker runs in the middleman, implementations should be careful to consume as little time as possible in message handlers.
Any considerable amount work should outsourced by spawning new actors (or maintaining worker actors).
Any considerable amount work should outsourced by spawning new actors (or maintaining worker actors).
All functions shown in this section can be accessed by including the header \lstinline^"caf/io/all.hpp"^ and live in the namespace \lstinline^caf::io^.
\subsection{Spawning Brokers}
\subsection{Spawning Brokers}
...
@@ -15,25 +17,25 @@ For convenience, \lstinline^spawn_io_server^ can be used to spawn a new broker l
...
@@ -15,25 +17,25 @@ For convenience, \lstinline^spawn_io_server^ can be used to spawn a new broker l
\begin{lstlisting}
\begin{lstlisting}
template<spawn_options Os = no_spawn_options,
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (io::broker*)>,
typename F = std::function<behavior (broker*)>,
typename... Ts>
typename... Ts>
actor spawn_io(F fun, Ts&&... args);
actor spawn_io(F fun, Ts&&... args);
template<spawn_options Os = no_spawn_options,
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (io::broker*)>,
typename F = std::function<behavior (broker*)>,
typename... Ts>
typename... Ts>
actor spawn_io_client(F fun,
actor spawn_io_client(F fun,
io::input_stream_ptr in,
input_stream_ptr in,
io::output_stream_ptr out,
output_stream_ptr out,
Ts&&... args);
Ts&&... args);
template<spawn_options Os = no_spawn_options,
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (io::broker*)>,
typename F = std::function<behavior (broker*)>,
typename... Ts>
typename... Ts>
actor spawn_io_client(F fun, string host, uint16_t port, Ts&&... args);
actor spawn_io_client(F fun, string host, uint16_t port, Ts&&... args);
template<spawn_options Os = no_spawn_options,
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (io::broker*)>,
typename F = std::function<behavior (broker*)>,
typename... Ts>
typename... Ts>
actor spawn_io_server(F fun, uint16_t port, Ts&&... args);
actor spawn_io_server(F fun, uint16_t port, Ts&&... args);
All actor operations as well as sending messages are network transparent.
All actor operations as well as sending messages are network transparent.
Remote actors are represented by actor proxies that forward all messages.
Remote actors are represented by actor proxies that forward all messages.
All functions shown in this section can be accessed by including the header \lstinline^"caf/io/all.hpp"^ and live in the namespace \lstinline^caf::io^.
\subsection{Publishing of Actors}
\subsection{Publishing of Actors}
...
@@ -15,10 +16,10 @@ The optional \lstinline^addr^ parameter can be used to listen only to the given
...
@@ -15,10 +16,10 @@ The optional \lstinline^addr^ parameter can be used to listen only to the given
Otherwise, the actor accepts all incoming connections (\lstinline^INADDR_ANY^).
Otherwise, the actor accepts all incoming connections (\lstinline^INADDR_ANY^).
@@ -11,7 +11,7 @@ Hence, we provide an internal domain-specific language to match incoming message
...
@@ -11,7 +11,7 @@ Hence, we provide an internal domain-specific language to match incoming message
\subsection{Basics}
\subsection{Basics}
\label{Sec::PatternMatching::Basics}
\label{Sec::PatternMatching::Basics}
A match expression begins with a call to the function \lstinline^on^, which returns an intermediate object providing the member function \lstinline^when^ and \lstinline^operator>>^.
A match expression begins with a call to the function \lstinline^on^, which returns an intermediate object providing \lstinline^operator>>^.
The right-hand side of the operator denotes a callback, usually a lambda expression, that should be invoked if a tuple matches the types given to \lstinline^on^,
The right-hand side of the operator denotes a callback, usually a lambda expression, that should be invoked if a tuple matches the types given to \lstinline^on^,
as shown in the example below.
as shown in the example below.
...
@@ -22,11 +22,11 @@ on<int, int, int>() >> [](int a, int b, int c) { /*...*/ }
...
@@ -22,11 +22,11 @@ on<int, int, int>() >> [](int a, int b, int c) { /*...*/ }
\end{lstlisting}
\end{lstlisting}
The result of \lstinline^operator>>^ is a \textit{match statement}.
The result of \lstinline^operator>>^ is a \textit{match statement}.
A partial function can consist of any number of match statements.
A message handler can consist of any number of match statements.
At most one callback is invoked, since the evaluation stops at the first match.
At most one callback is invoked, since the evaluation stops at the first match.
\begin{lstlisting}
\begin{lstlisting}
partial_function fun {
message_handler fun {
on<int>() >> [](int i) {
on<int>() >> [](int i) {
// case1
// case1
},
},
...
@@ -129,106 +129,7 @@ others() >> [] {
...
@@ -129,106 +129,7 @@ others() >> [] {
}
}
\end{lstlisting}
\end{lstlisting}
\subsection{Guards}
\clearpage
Guards can be used to constrain a given match statement by using placeholders, as the following example illustrates.
\begin{lstlisting}
using namespace cppa::placeholders; // contains _x1 - _x9
on<int>().when(_x1 % 2 == 0) >> [] {
// int is even
},
on<int>() >> [] {
// int is odd
}
\end{lstlisting}
Guard expressions are a lazy evaluation technique.
The placeholder \lstinline^_x1^ is substituted with the first value of a given tuple.
All binary comparison and arithmetic operators are supported, as well as \lstinline^&&^ and \lstinline^||^.
In addition, there are three functions designed to be used in guard expressions: \lstinline^gref^ (``guard reference''), \lstinline^gval^ (``guard value''), and \lstinline^gcall^ (``guard function call'').
The function \lstinline^gref^ creates a reference wrapper, while \lstinline^gval^ encloses a value.
It is similar to \lstinline^std::ref^ but it is always \lstinline^const^ and ``lazy''.
A few examples to illustrate some pitfalls:
\begin{lstlisting}
int val = 42;
on<int>().when(_x1 == val) // (1) matches if _x1 == 42
on<int>().when(_x1 == gref(val)) // (2) matches if _x1 == val
on<int>().when(_x1 == std::ref(val)) // (3) ok, because of placeholder
Statement \texttt{(5)} is evaluated immediately and returns a boolean, whereas statement \texttt{(4)} creates a valid guard expression.
Thus, you should always use \lstinline^gref^ instead of \lstinline^std::ref^ to avoid errors.
The second function, \lstinline^gcall^, encapsulates a function call.
Its usage is similar to \lstinline^std::bind^, but there is also a short version for unary functions: \lstinline^gcall(fun, _x1)^ is equal to \lstinline^_x1(fun)^.
\begin{lstlisting}
auto vec_sorted = [](const std::vector<int>& vec) {
return std::is_sorted(vec.begin(), vec.end());
};
on<std::vector<int>>().when(gcall(vec_sorted, _x1)) // is equal to:
\multicolumn{2}{m{\linewidth}}{\large{\textbf{Member functions}\small{(\lstinline^x^ represents the value at runtime, \lstinline^y^ represents an iterable container)}}\vspace{3pt}}\\
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^.
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^message^, because it creates a new tuple containing the old one.
\begin{lstlisting}
\begin{lstlisting}
void some_fun(event_based_actor* self) {
void some_fun(event_based_actor* self) {
actor other = spawn(...);
actor other = spawn(...);
auto msg = make_any_tuple(1, 2, 3);
auto msg = make_message(1, 2, 3);
self->send(other, msg); // oops, creates a new tuple containing msg
self->send(other, msg); // oops, creates a new tuple containing msg