Commit d7e3c1f6 authored by Dominik Charousset's avatar Dominik Charousset

Update manual to reflect 0.10 API

parent eaaa2c3c
...@@ -9,7 +9,7 @@ build-gcc/* ...@@ -9,7 +9,7 @@ build-gcc/*
Makefile Makefile
bin/* bin/*
lib/* lib/*
manual/manual.pdf
manual/build-pdf/* manual/build-pdf/*
manual/build-html/* manual/build-html/*
manual/*.toc
*.swp *.swp
...@@ -34,7 +34,7 @@ class local_actor; ...@@ -34,7 +34,7 @@ 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^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 \\
\hline \hline
......
...@@ -9,18 +9,18 @@ Event-based actors differ in receiving messages from context-switching and threa ...@@ -9,18 +9,18 @@ Event-based actors differ in receiving messages from context-switching and threa
\subsection{Receiving Messages} \subsection{Receiving Messages}
The function \lstinline^receive^ sequentially iterates over all elements in the mailbox beginning with the first. The function \lstinline^receive^ sequentially iterates over all elements in the mailbox beginning with the first.
It takes a partial function that is applied to the elements in the mailbox until an element was matched by the partial function. It takes a message handler that is applied to the elements in the mailbox until an element was matched by the handler.
An actor calling \lstinline^receive^ is blocked until it successfully dequeued a message from its mailbox or an optional timeout occurs. An actor calling \lstinline^receive^ is blocked until it successfully dequeued a message from its mailbox or an optional timeout occurs.
\begin{lstlisting} \begin{lstlisting}
self->receive ( self->receive (
on<int>().when(_x1 > 0) >> // ... on<int>() >> // ...
); );
\end{lstlisting} \end{lstlisting}
The code snippet above illustrates the use of \lstinline^receive^. The code snippet above illustrates the use of \lstinline^receive^.
Note that the partial function passed to \lstinline^receive^ is a temporary object at runtime. Note that the message handler passed to \lstinline^receive^ is a temporary object at runtime.
Hence, using receive inside a loop would cause creation of a new partial function on each iteration. Hence, using receive inside a loop would cause creation of a new handler on each iteration.
\lib provides three predefined receive loops to provide a more efficient but yet convenient way of defining receive loops. \lib provides three predefined receive loops to provide a more efficient but yet convenient way of defining receive loops.
\begin{tabular*}{\textwidth}{p{0.47\textwidth}|p{0.47\textwidth}} \begin{tabular*}{\textwidth}{p{0.47\textwidth}|p{0.47\textwidth}}
...@@ -72,7 +72,7 @@ do_receive ( ...@@ -72,7 +72,7 @@ do_receive (
others() >> [&]() { others() >> [&]() {
++received; ++received;
} }
).until(gref(received) >= 10); ).until([&] { return received >= 10; });
\end{lstlisting} \\ \end{lstlisting} \\
\end{tabular*} \end{tabular*}
......
...@@ -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)^.
\end{itemize} \end{itemize}
......
\section{Copy-On-Write Tuples}
\label{Sec::Tuples}
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>
any_tuple x2 = x1; // any_tuple
any_tuple x3 = make_cow_tuple(10, 20); // any_tuple
auto x4 = make_any_tuple(42); // any_tuple
\end{lstlisting}
\clearpage
\subsection{Casting Tuples}
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.
\begin{lstlisting}
auto x1 = make_cow_tuple(1, 2, 3);
any_tuple x2 = x1;
auto x3_opt = tuple_cast<int, anything, int>(x2);
assert(x3_opt.valid());
auto x3 = *x3_opt;
assert(get<0>(x3) == 1);
assert(get<1>(x3) == 3);
assert(&get<0>(x3) == &get<0>(x1));
assert(&get<1>(x3) == &get<2>(x1));
\end{lstlisting}
...@@ -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);
\end{lstlisting} \end{lstlisting}
...@@ -42,7 +44,7 @@ actor spawn_io_server(F fun, uint16_t port, Ts&&... args); ...@@ -42,7 +44,7 @@ actor spawn_io_server(F fun, uint16_t port, Ts&&... args);
\subsection{Broker Interface} \subsection{Broker Interface}
\begin{lstlisting} \begin{lstlisting}
class io::broker; class broker;
\end{lstlisting} \end{lstlisting}
{\small {\small
...@@ -89,8 +91,8 @@ However, it also receives system messages from the middleman: ...@@ -89,8 +91,8 @@ However, it also receives system messages from the middleman:
\begin{lstlisting} \begin{lstlisting}
struct new_connection_msg { struct new_connection_msg {
io::accept_handle source; accept_handle source;
io::connection_handle handle; connection_handle handle;
}; };
\end{lstlisting} \end{lstlisting}
...@@ -98,7 +100,7 @@ A \lstinline^new_connection_msg^ is received whenever a new incoming connection ...@@ -98,7 +100,7 @@ A \lstinline^new_connection_msg^ is received whenever a new incoming connection
\begin{lstlisting} \begin{lstlisting}
struct new_data_msg { struct new_data_msg {
io::connection_handle handle; connection_handle handle;
util::buffer buf; util::buffer buf;
}; };
\end{lstlisting} \end{lstlisting}
...@@ -112,7 +114,7 @@ This means, as long as the broker does not create any new references to the mess ...@@ -112,7 +114,7 @@ This means, as long as the broker does not create any new references to the mess
\begin{lstlisting} \begin{lstlisting}
struct connection_closed_msg { struct connection_closed_msg {
io::connection_handle handle; connection_handle handle;
}; };
\end{lstlisting} \end{lstlisting}
...@@ -120,7 +122,7 @@ A \lstinline^connection_closed_msg^ informs the broker that one of its connectio ...@@ -120,7 +122,7 @@ A \lstinline^connection_closed_msg^ informs the broker that one of its connectio
\begin{lstlisting} \begin{lstlisting}
struct acceptor_closed_msg { struct acceptor_closed_msg {
io::accept_handle handle; accept_handle handle;
}; };
\end{lstlisting} \end{lstlisting}
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
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^).
\begin{lstlisting} \begin{lstlisting}
publish(self, 4242); io::publish(self, 4242);
self->become ( self->become (
on(atom("ping"), arg_match) >> [](int i) { on(atom("ping"), arg_match) >> [](int i) {
return make_cow_tuple(atom("pong"), i); return make_message(atom("pong"), i);
} }
); );
\end{lstlisting} \end{lstlisting}
...@@ -40,7 +41,7 @@ self->become ( ...@@ -40,7 +41,7 @@ self->become (
self->quit(); self->quit();
}, },
on(atom("pong"), arg_match) >> [=](int i) { on(atom("pong"), arg_match) >> [=](int i) {
return make_cow_tuple(atom("ping"), i+1); return make_message(atom("ping"), i+1);
} }
); );
\end{lstlisting} \end{lstlisting}
...@@ -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
others().when(gref(val) == 42) // (4) matches everything
// as long as val == 42
others().when(std::ref(val) == 42) // (5) compiler error
\end{lstlisting}
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:
on<std::vector<int>>().when(_x1(vec_sorted)))
\end{lstlisting}
\subsubsection{Placeholder Interface}
\begin{lstlisting}
template<int X>
struct guard_placeholder;
\end{lstlisting}
\begin{tabular*}{\textwidth}{m{0.3\linewidth}m{0.7\linewidth}}
\multicolumn{2}{m{\linewidth}}{\large{\textbf{Member functions} \small{(\lstinline^x^ represents the value at runtime, \lstinline^y^ represents an iterable container)}}\vspace{3pt}} \\
\hline
\lstinline^size()^ & Returns \lstinline^x.size()^ \\
\hline
\lstinline^empty()^ & Returns \lstinline^x.empty()^ \\
\hline
\lstinline^not_empty()^ & Returns \lstinline^!x.empty()^ \\
\hline
\lstinline^front()^ & Returns an \lstinline^option^ (see Section \ref{Appendix::Option}) to \lstinline^x.front()^ \\
\hline
\lstinline^in(y)^ & Returns \lstinline^true^ if \lstinline^y^ contains \lstinline^x^, \lstinline^false^ otherwise\\
\hline
\lstinline^not_in(y)^ & Returns \lstinline^!in(y)^ \\
\hline
\\
\end{tabular*}
\subsubsection{Examples for Guard Expressions}
\begin{lstlisting}
using namespace std;
typedef vector<int> ivec;
vector<string> strings{"abc", "def"};
on_arg_match.when(_x1.front() == 0) >> [](const ivec& v) {
// note: we don't have to check whether _x1 is empty in our guard,
// because '_x1.front()' returns an option for a
// reference to the first element
assert(v.size() >= 1);
assert(v.front() == 0);
},
on<int>().when(_x1.in({10, 20, 30})) >> [](int i) {
assert(i == 10 || i == 20 || i == 30);
},
on<string>().when(_x1.not_in(strings)) >> [](const string& str) {
assert(str != "abc" && str != "def");
},
on<string>().when(_x1.size() == 10) >> [](const string& str) {
// ...
}
\end{lstlisting}
\subsection{Projections and Extractors} \subsection{Projections and Extractors}
Projections perform type conversions or extract data from a given input. Projections perform type conversions or extract data from a given input.
...@@ -244,7 +145,7 @@ auto intproj = [](const string& str) -> option<int> { ...@@ -244,7 +145,7 @@ auto intproj = [](const string& str) -> option<int> {
if (endptr != nullptr && *endptr == '\0') return result; if (endptr != nullptr && *endptr == '\0') return result;
return {}; return {};
}; };
partial_function fun { message_handler fun {
on(intproj) >> [](int i) { on(intproj) >> [](int i) {
// case 1: successfully converted a string // case 1: successfully converted a string
}, },
......
...@@ -12,12 +12,12 @@ void send(actor whom, Args&&... what); ...@@ -12,12 +12,12 @@ void send(actor whom, Args&&... what);
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
self->send_tuple(other, msg); // ok self->send_tuple(other, msg); // ok
} }
......
...@@ -20,7 +20,7 @@ auto p0 = spawn_typed( ...@@ -20,7 +20,7 @@ auto p0 = spawn_typed(
return static_cast<double>(a) * b; return static_cast<double>(a) * b;
}, },
on_arg_match >> [](double a, double b) { on_arg_match >> [](double a, double b) {
return make_cow_tuple(a * b, a / b); return std::make_tuple(a * b, a / b);
} }
); );
// assign to identical type // assign to identical type
......
...@@ -8,7 +8,7 @@ The member functions \lstinline^sync_send^ and \lstinline^sync_send_tuple^ send ...@@ -8,7 +8,7 @@ The member functions \lstinline^sync_send^ and \lstinline^sync_send_tuple^ send
template<typename... Args> template<typename... Args>
__unspecified__ sync_send(actor whom, Args&&... what); __unspecified__ sync_send(actor whom, Args&&... what);
__unspecified__ sync_send_tuple(actor whom, any_tuple what); __unspecified__ sync_send_tuple(actor whom, message what);
template<typename Duration, typename... Args> template<typename Duration, typename... Args>
__unspecified__ timed_sync_send(actor whom, __unspecified__ timed_sync_send(actor whom,
...@@ -18,7 +18,7 @@ __unspecified__ timed_sync_send(actor whom, ...@@ -18,7 +18,7 @@ __unspecified__ timed_sync_send(actor whom,
template<typename Duration, typename... Args> template<typename Duration, typename... Args>
__unspecified__ timed_sync_send_tuple(actor whom, __unspecified__ timed_sync_send_tuple(actor whom,
Duration timeout, Duration timeout,
any_tuple what); message what);
\end{lstlisting} \end{lstlisting}
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.
......
No preview for this file type
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