Commit 1e828b18 authored by Dominik Charousset's avatar Dominik Charousset

Move manual sources to separate submodule

parent 01aef170
*.DS_Store *.DS_Store
Doxyfile Doxyfile
cppa.creator.user*
html/ html/
build/* build/*
cli_build/* cli_build/*
...@@ -9,11 +8,7 @@ build-gcc/* ...@@ -9,11 +8,7 @@ build-gcc/*
Makefile Makefile
bin/* bin/*
lib/* lib/*
manual/build-pdf/* manual/*
manual/build-html/*
manual/*.toc
manual/manual.html
manual/variables.tex
*.swp *.swp
bii/* bii/*
libcaf_core/caf/detail/build_config.hpp libcaf_core/caf/detail/build_config.hpp
%\section{Actor Companions}
%\subsection{Treat Qt GUI Elements as Actors}
\section{Management \& Error Detection}
\lib adapts Erlang's well-established fault propagation model.
It allows to build actor subsystem in which either all actors are alive or have collectively failed.
\subsection{Links}
Linked actors monitor each other.
An actor sends an exit message to all of its links as part of its termination.
The default behavior for actors receiving such an exit message is to die for the same reason, if the exit reason is non-normal.
Actors can \textit{trap} exit messages to handle them manually.
\begin{lstlisting}
actor worker = ...;
// receive exit messages as regular messages
self->trap_exit(true);
// monitor spawned actor
self->link_to(worker);
// wait until worker exited
self->become (
[=](const exit_msg& e) {
if (e.reason == exit_reason::normal) {
// worker finished computation
else {
// worker died unexpectedly
}
}
);
\end{lstlisting}
\subsection{Monitors}
\label{Sec::Management::Monitors}
A monitor observes the lifetime of an actor.
Monitored actors send a down message to all observers as part of their termination.
Unlike exit messages, down messages are always treated like any other ordinary message.
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.
\begin{lstlisting}
actor worker = ...;
// monitor spawned actor
self->monitor(worker);
// wait until worker exited
self->become (
[](const down_msg& d) {
if (d.reason == exit_reason::normal) {
// worker finished computation
} else {
// worker died unexpectedly
}
}
);
\end{lstlisting}
\subsection{Error Codes}
All error codes are defined in the namespace \lstinline^caf::exit_reason^.
To obtain a string representation of an error code, use \lstinline^caf::exit_reason::as_string(uint32_t)^.
\begin{tabular*}{\textwidth}{m{0.35\textwidth}m{0.08\textwidth}m{0.5\textwidth}}
\hline
\lstinline^normal^ & 1 & Actor finished execution without error \\
\hline
\lstinline^unhandled_exception^ & 2 & Actor was killed due to an unhandled exception \\
\hline
\lstinline^unhandled_sync_failure^ & 4 & Actor was killed due to an unexpected synchronous response message \\
\hline
\lstinline^unhandled_sync_timeout^ & 5 & Actor was killed, because no timeout handler was set and a synchronous message timed out \\
\hline
\lstinline^unknown^ & 6 & Indicates that an actor has been exited and its state is no longer known \\
\hline
\lstinline^out_of_workers^ & 7 & Indicates that an actor pool unexpectedly ran out of workers \\
\hline
\lstinline^user_shutdown^ & 16 & Actor was killed by a user-generated event \\
\hline
\lstinline^kill^ & 17 & Unconditionally kills actors when using in an \lstinline^exit_msg^, even when trapping exits \\
\hline
\lstinline^remote_link_unreachable^ & 257 & Indicates that a remote actor became unreachable, e.g., due to connection error \\
\hline
\lstinline^user_defined^ & 65536 & Minimum value for user-defined exit codes \\
\hline
\end{tabular*}
\subsection{Attach Cleanup Code to an Actor}
Actors can attach cleanup code to other actors.
This code is executed immediately if the actor has already exited.
\begin{lstlisting}
using done_atom = atom_constant<atom("done")>;
behavior supervisor(event_based_actor* self, actor worker) {
actor observer = self;
// "monitor" spawned actor
worker->attach_functor([observer](std::uint32_t reason) {
// this callback is invoked from worker
anon_send(observer, done_atom::value);
});
// wait until worker exited
return {
[](done_atom) {
// ... worker terminated ...
}
};
}
\end{lstlisting}
\textbf{Note}: It is possible to attach code to remote actors, but the cleanup code will run on the local machine.
\section{Actors}
\label{Sec::Actors}
\lib provides several actor implementations, each covering a particular use case.
The class \lstinline^local_actor^ is the base class for all implementations, except for (remote) proxy actors.
Hence, \lstinline^local_actor^ provides a common interface for actor operations like trapping exit messages or finishing execution.
The default actor implementation in \lib is event-based.
Event-based actors have a very small memory footprint and are thus very lightweight and scalable.
Context-switching actors are used for actors that make use of the blocking API (see Section \ref{Sec::BlockingAPI}), but do not need to run in a separate thread.
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{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.
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
\subsection{Interface}
\label{Sec::Actors::Interfaces}
\begin{lstlisting}
class local_actor;
\end{lstlisting}
{\small
\begin{tabular*}{\textwidth}{m{0.45\textwidth}m{0.5\textwidth}}
\multicolumn{2}{m{\linewidth}}{\large{\textbf{Member functions}}\vspace{3pt}} \\
\\
\multicolumn{2}{l}{\textbf{Observers}\vspace{3pt}} \\
\hline
\lstinline^actor_addr address()^ & Returns the address of this actor \\
\hline
\lstinline^bool trap_exit()^ & Checks whether this actor traps exit messages \\
\hline
\lstinline^message& current_message()^ & Returns the currently processed message\newline\textbf{Warning}: Only set during callback invocation; calling this function after forwarding the message or while not in a callback is undefined behavior \\
\hline
\lstinline^actor_addr& current_sender()^ & Returns the sender of the current message\newline\textbf{Warning}: Only set during callback invocation; calling this function after forwarding the message or while not in a callback is undefined behavior \\
\hline
\lstinline^vector<group> joined_groups()^ & Returns all subscribed groups \\
\hline
\\
\multicolumn{2}{l}{\textbf{Modifiers}\vspace{3pt}} \\
\hline
\lstinline^quit(uint32_t reason = normal)^ & Finishes execution of this actor \\
\hline
\lstinline^void trap_exit(bool enabled)^ & Enables or disables trapping of exit messages \\
\hline
\lstinline^void join(const group& g)^ & Subscribes to group \lstinline^g^ \\
\hline
\lstinline^void leave(const group& g)^ & Unsubscribes group \lstinline^g^ \\
\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^) \\
\hline
\lstinline^void monitor(actor whom)^ & Unidirectionally monitors \lstinline^whom^ (see Section \ref{Sec::Management::Monitors}) \\
\hline
\lstinline^void demonitor(actor whom)^ & Removes a monitor from \lstinline^whom^ \\
\hline
\lstinline^bool has_sync_failure_handler()^ & Checks whether this actor has a user-defined sync failure handler \\
\hline
\lstinline^template <class F>^ \lstinline^void set_exception_handler(F f)^ & Sets a custom handler for uncaught exceptions \\
\hline
\lstinline^void on_exit()^ & Can be overridden to add cleanup code that runs after an actor finished execution, e.g., to break cycles \\
\hline
\end{tabular*}
}
This diff is collapsed.
\section{Blocking API}
\label{Sec::BlockingAPI}
Besides event-based actors (the default implementation), \lib also provides context-switching and thread-mapped actors that can make use of the blocking API.
Those actor implementations are intended to ease migration of existing applications or to implement actors that need to have access to blocking receive primitives for other reasons.
Event-based actors differ in receiving messages from context-switching and thread-mapped actors: the former define their behavior as a message handler that is invoked whenever a new messages arrives in the actor's mailbox (by using \lstinline^become^), whereas the latter use an explicit, blocking receive function.
\subsection{Receiving Messages}
The function \lstinline^receive^ sequentially iterates over all elements in the mailbox beginning with the first.
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.
\begin{lstlisting}
self->receive (
on<int>() >> // ...
);
\end{lstlisting}
The code snippet above illustrates the use of \lstinline^receive^.
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 handler on each iteration.
\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}}
\lstinline^// DON'T^ & \lstinline^// DO^ \\
\begin{lstlisting}
for (;;) {
receive (
// ...
);
}
\end{lstlisting} & %
\begin{lstlisting}
receive_loop (
// ...
);
\end{lstlisting} \\
\begin{lstlisting}
std::vector<int> results;
for (size_t i = 0; i < 10; ++i) {
receive (
on<int>() >> [&](int value) {
results.push_back(value);
}
);
}
\end{lstlisting} & %
\begin{lstlisting}
std::vector<int> results;
size_t i = 0;
receive_for(i, 10) (
on<int>() >> [&](int value) {
results.push_back(value);
}
);
\end{lstlisting} \\
\begin{lstlisting}
size_t received = 0;
do {
receive (
others >> [&]() {
++received;
}
);
} while (received < 10);
\end{lstlisting} & %
\begin{lstlisting}
size_t received = 0;
do_receive (
others >> [&]() {
++received;
}
).until([&] { return received >= 10; });
\end{lstlisting} \\
\end{tabular*}
The examples above illustrate the correct usage of the three loops \lstinline^receive_loop^, \lstinline^receive_for^ and \lstinline^do_receive(...).until^.
It is possible to nest receives and receive loops.
\begin{lstlisting}
self->receive_loop (
on<int>() >> [&](int value1) {
self->receive (
on<float>() >> [&](float value2) {
cout << value1 << " => " << value2 << endl;
}
);
}
);
\end{lstlisting}
\clearpage
\subsection{Receiving Synchronous Responses}
Analogous to \lstinline^sync_send(...).then(...)^ for event-based actors, blocking actors can use \lstinline^sync_send(...).await(...)^.
\begin{lstlisting}
void foo(blocking_actor* self, actor testee) {
// testee replies with a string to 'get'
self->sync_send(testee, get_atom::value).await(
[&](const std::string& str) {
// handle str
},
after(std::chrono::seconds(30)) >> [&]() {
// handle error
}
);
}
\end{lstlisting}
\subsection{Mixing Actors and Threads with Scoped Actors}
The class \lstinline^scoped_actor^ offers a simple way of communicating with CAF actors from non-actor contexts.
It overloads \lstinline^operator->^ to return a \lstinline^blocking_actor*^.
Hence, it behaves like the implicit \lstinline^self^ pointer in functor-based actors, only that it ceases to exist at scope end.
\begin{lstlisting}
void test() {
scoped_actor self;
// spawn some monitored actor
auto aut = self->spawn<monitored>(my_actor_impl);
self->sync_send(aut, "hi there").await(
... // handle response
);
// self will be destroyed automatically here; any
// actor monitoring it will receive down messages etc.
}
\end{lstlisting}
Note that \lstinline^scoped_actor^ throws an \lstinline^actor_exited^ exception when forced to quit for some reason, e.g., via an \lstinline^exit_msg^.
Whenever a \lstinline^scoped_actor^ might end up receiving an \lstinline^exit_msg^ (because it links itself to another actor for example), the caller either needs to handle the exception or the actor needs to process \lstinline^exit_msg^ manually via \lstinline^self->trap_exit(true)^.
\section{Common Pitfalls}
\label{Sec::Pitfalls}
\subsection{Defining Patterns}
\begin{itemize}
\item C++ evaluates comma-separated expressions from left-to-right, using only the last element as return type of the whole expression. This means that message handlers and behaviors must \emph{not} be initialized like this:
\begin{lstlisting}
message_handler wrong = (
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
\end{lstlisting}
The correct way to initialize message handlers and behaviors is to either use the constructor or the member function \lstinline^assign^:
\begin{lstlisting}
message_handler ok1{
[](int i) { /*...*/ },
[](float f) { /*...*/ }
};
message_handler ok2;
// some place later
ok2.assign(
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
\end{lstlisting}
\end{itemize}
\subsection{Event-Based API}
\begin{itemize}
\item The functions \lstinline^become^ and \lstinline^handle_response^ do not block, i.e., always return immediately.
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{Synchronous Messages}
\begin{itemize}
\item
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.
\item
The handle returned by \lstinline^sync_send^ is bound to the calling actor.
It is not possible to transfer a handle to a response to another actor.
\end{itemize}
\clearpage
\subsection{Sharing}
\begin{itemize}
\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.
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 its lifetime is guaranteed to outlive all actors.
The simplest way to meet the lifetime guarantee is by storing the data in smart pointers such as \lstinline^std::shared_ptr^.
Nevertheless, the recommended way of sharing informations is message passing.
Sending the same message to multiple actors does not result in copying the data several times.
\end{itemize}
\subsection{Constructors of Class-based Actors}
\begin{itemize}
\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.
\end{itemize}
\section{First Steps}
%\subsection{Why Actors?}
%\subsection{Some Terminology}
To compile \lib, you will need CMake and a C++11 compiler. To get and compile the sources, open a terminal (on Linux or Mac OS X) and type:
\begin{verbatim}
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
\end{verbatim}
It is recommended to run the unit tests as well:
\begin{verbatim}
make test
\end{verbatim}
Please submit a bug report that includes (a) your compiler version, (b) your OS, and (c) the content of the file \texttt{build/Testing/Temporary/LastTest.log} if an error occurs.
\subsection{Features Overview}
\begin{itemize*}
\item Lightweight, fast and efficient actor implementations
\item Network transparent messaging
\item Error handling based on Erlang's failure model
\item Pattern matching for messages as internal DSL to ease development
\item Thread-mapped actors for soft migration of existing applications
\item Publish/subscribe group communication
\end{itemize*}
\subsection{Supported Compilers}
\begin{itemize*}
\item GCC $\ge$ 4.8
\item Clang $\ge$ 3.2
\end{itemize*}
\subsection{Supported Operating Systems}
\begin{itemize*}
\item Linux
\item Mac OS X
\item \textit{Note for MS Windows}:
\lib relies on C++11 features such as unrestricted unions.
We will support this platform as soon as Microsoft's compiler implements all required C++11 features.
In the meantime, \lib can be used with MinGW.
\end{itemize*}
\clearpage
\subsection{Hello World Example}
\begin{lstlisting}
#include <string>
#include <iostream>
#include "caf/all.hpp"
using namespace std;
using namespace caf;
behavior mirror(event_based_actor* self) {
// return the (initial) actor behavior
return {
// a handler for messages containing a single string
// that replies with a string
[=](const string& what) -> string {
// prints "Hello World!" via aout
// (thread-safe cout wrapper)
aout(self) << what << endl;
// terminates this actor
// ('become' otherwise loops forever)
self->quit();
// reply "!dlroW olleH"
return string(what.rbegin(), what.rend());
}
};
}
void hello_world(event_based_actor* self, const actor& buddy) {
// send "Hello World!" to our buddy ...
self->sync_send(buddy, "Hello World!").then(
// ... wait for a response ...
[=](const string& what) {
// ... and print it
aout(self) << what << endl;
}
);
}
int main() {
// create a new actor that calls 'mirror()'
auto mirror_actor = spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)';
spawn(hello_world, mirror_actor);
// wait until all other actors we have spawned are done
await_all_actors_done();
// run cleanup code before exiting main
shutdown();
}
\end{lstlisting}
\section{Group Communication}
\label{Sec::Group}
\lib supports publish/subscribe-based group communication.
Actors can join and leave groups and send messages to groups.
\begin{lstlisting}
std::string group_module = ...;
std::string group_id = ...;
auto grp = group::get(group_module, group_id);
self->join(grp);
self->send(grp, "test");
self->leave(grp);
\end{lstlisting}
\subsection{Anonymous Groups}
\label{Sec::Group::Anonymous}
Groups created on-the-fly with \lstinline^group::anonymous()^ can be used to coordinate a set of workers.
Each call to \lstinline^group::anonymous()^ returns a new, unique group instance.
\subsection{Local Groups}
\label{Sec::Group::Local}
The \lstinline^"local"^ group module creates groups for in-process communication.
For example, a group for GUI related events could be identified by \lstinline^group::get("local", "GUI events")^.
The group ID \lstinline^"GUI events"^ uniquely identifies a singleton group instance of the module \lstinline^"local"^.
\subsection{Remote Groups}
\label{Sec::Group::RemoteGroups}
To deploy groups in a network, one host can act as group server by publishing its local groups at any given port:
\begin{lstlisting}
void publish_local_groups(std::uint16_t port, const char* addr)
\end{lstlisting}
By calling \lstinline^group::get("remote", "<group>@<host>:<port>")^, other hosts are now able to connect to a remotely running group.
Please note that the group communication is no longer available once the server disconnects.
This implementation uses N-times unicast underneath.
It is worth mentioning that user-implemented groups can be build on top of IP multicast or overlay technologies such as Scribe to achieve better performance or reliability.
\clearpage
\subsection{Spawning Actors in Groups}
\label{Sec::Group::Spawn}
The function \lstinline^spawn_in_group^ can be used to create actors as members of a group.
The function causes the newly created actors to call \lstinline^join(...)^ immediately and before \lstinline^spawn_in_group^ returns.
The usage of \lstinline^spawn_in_group^ is equal to \lstinline^spawn^, except for an additional group argument.
The group handle is always the first argument, as shown in the examples below.
\begin{lstlisting}
void fun1();
void fun2(int, float);
class my_actor1 : event_based_actor { /* ... */ };
class my_actor2 : event_based_actor {
// ...
my_actor2(const std::string& str) { /* ... */ }
};
// ...
auto grp = group::get(...);
auto a1 = spawn_in_group(grp, fun1);
auto a2 = spawn_in_group(grp, fun2, 1, 2.0f);
auto a3 = spawn_in_group<my_actor1>(grp);
auto a4 = spawn_in_group<my_actor2>(grp, "hello my_actor2!");
\end{lstlisting}
\section{Introduction}
Before diving into the API of \lib, we would like to take the opportunity to discuss the concepts behind \lib 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 other threading 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 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.
However, the actor model has not yet been widely adopted in the native programming domain.
With \lib, 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, \lib follows the C++ philosophy ``building the highest abstraction possible without sacrificing performance''.
\subsection{Terminology}
You will find that \lib has not simply adopted exiting implementations based on the actor model such as Erlang or the Akka library.
Instead, \lib 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 \lib nor this manual require any foreknowledge.
\subsubsection{Actor Address}
In \lib, 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 \lib 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}
``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.
DIRS := build-html build-pdf
SOURCES = ActorManagement.tex Actors.tex Appendix.tex CommonPitfalls.tex FirstSteps.tex GroupCommunication.tex MessagePriorities.tex NetworkTransparency.tex PatternMatching.tex ReceivingMessages.tex SendingMessages.tex StronglyTypedActors.tex SpawningActors.tex SynchronousMessages.tex TypeSystem.tex manual.tex
all: pdf html
pdf: manual.pdf
html: manual.html
clean:
@for i in $(DIRS); do $(MAKE) -C $$i $@; done
rm -f manual.pdf
rm -f manual.html
manual.pdf: $(SOURCES)
$(MAKE) -C build-pdf && cp build-pdf/manual.pdf manual.pdf
manual.html: $(SOURCES)
$(MAKE) -C build-html && cp build-html/manual.html manual.html
.PHONY: all clean
\section{Managing Groups of Workers}
\label{Sec::WorkerGroups}
When managing a set of workers, a central actor often dispatches requests to a set of workers.
For this purpose, the class \lstinline^actor_pool^ implements a lightweight abstraction for managing a set of workers using a dispatching policy. Unlike groups, pools usually own their workers.
Pools are created using the static member function \lstinline^make^, which takes either one argument (the policy) or three (number of workers, factory function for workers, and dispatching policy).
After construction, one can add new workers via messages of the form $('SYS', 'PUT', worker)$, remove workers with $('SYS', 'DELETE', worker)$, and retrieve the set of workers as \lstinline^vector<actor>^ via $('SYS', 'GET')$.
For example, \lstinline^send(my_pool, sys_atom::value, put_atom::value, worker)^ adds \lstinline^worker^ to \lstinline^my_pool^.
An actor pool takes ownership of its workers.
When forced to quit, it sends an exit messages to all of its workers, forcing them to quit as well.
The pool also monitors all of its workers.
Pools do not cache messages, but enqueue them directly in a workers mailbox. Consequently, a terminating worker loses all unprocessed messages. For more advanced caching strategies, such as reliable message delivery, users can implement their own dispatching policies.
\subsection{Dispatching Policies}
A dispatching policy is a functor with the following signature:
\begin{lstlisting}
using uplock = upgrade_lock<detail::shared_spinlock>;
using policy = std::function<void (uplock& guard,
const actor_vec& workers,
mailbox_element_ptr& ptr,
execution_unit* host)>;
\end{lstlisting}
The argument \lstinline^guard^ is a shared lock that can be upgraded for unique access if the policy includes a critical section. The second argument is a vector containing all workers managed by the pool. The argument \lstinline^ptr^ contains the full message as received by the pool. Finally, \lstinline^host^ is the current scheduler context that can be used to enqueue workers into the corresponding job queue.
The actor pool class comes with a set predefined policies, accessible via factory functions, for convenience.
\begin{lstlisting}
actor_pool::policy actor_pool::round_robin();
\end{lstlisting}
This policy forwards incoming requests in a round-robin manner to workers.
There is no guarantee that messages are consumed, i.e., work items are lost if the worker exits before processing all of its messages.
\begin{lstlisting}
actor_pool::policy actor_pool::broadcast();
\end{lstlisting}
This policy forwards \emph{each} message to \emph{all} workers.
Synchronous messages to the pool will be received by all workers, but the client will only recognize the first arriving response message---or error---and discard subsequent messages.
Note that this is not caused by the policy itself, but a consequence of forwarding synchronous messages to more than one actor.
\begin{lstlisting}
actor_pool::policy actor_pool::random();
\end{lstlisting}
This policy forwards incoming requests to one worker from the pool chosen uniformly at random.
Analogous to \lstinline^round_robin^, this policy does not cache or redispatch messages.
\begin{lstlisting}
using join = function<void (T&, message&)>;
using split = function<void (vector<pair<actor, message>>&, message&)>;
template <class T>
static policy split_join(join jf, split sf = ..., T init = T());
\end{lstlisting}
This policy models split/join or scatter/gather work flows, where a work item is split into as many tasks as workers are available and then the individuals results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together to create a single result. The function is called with the result object (initialed using \lstinline^init^) and the current result messages from a worker.
The first argument of a split function is a mapping from actors (workers) to tasks (messages). The second argument is the input message. The default split function is a broadcast dispatching, sending each worker the original request.
\clearpage
\subsection{Example}
\begin{lstlisting}
actor new_worker() {
return spawn([]() -> behavior {
return {
[](int x, int y) {
return x + y;
}
};
});
}
void broadcast_example() {
scoped_actor self;
// spawns a pool with 5 workers
auto pool5 = [] {
return actor_pool::make(5, new_worker, actor_pool::broadcast());
};
// spawns a pool with 5 pools with 5 workers each
auto w = actor_pool::make(5, pool5, actor_pool::broadcast());
// will be broadcasted to 25 workers
self->send(w, 1, 2);
std::vector<int> results;
int i = 0;
self->receive_for(i, 25)(
[&](int res) {
results.push_back(res);
}
);
assert(results.size(), 25);
assert(std::all_of(results.begin(), results.end(),
[](int res) { return res == 3; }));
// terminate pool(s) and all workers
self->send_exit(w, exit_reason::user_shutdown);
}
\end{lstlisting}
\section{Message Priorities}
By default, all messages have the same priority and actors ignore priority flags.
Actors that should evaluate priorities must be spawned using the \lstinline^priority_aware^ flag.
This flag causes the actor to use a priority-aware mailbox implementation.
It is not possible to change this implementation dynamically at runtime.
\begin{lstlisting}
using a_atom = atom_constant<atom("a")>;
using b_atom = atom_constant<atom("b")>;
behavior testee(event_based_actor* self) {
// send 'b' with normal priority
self->send(self, b_atom::value);
// send 'a' with high priority
self->send(message_priority::high, self, a_atom::value);
// terminate after receiving a 'b'
return {
[=](b_atom) {
aout(self) << "received 'b' => quit" << endl;
self->quit();
},
[=](a_atom) {
aout(self) << "received 'a'" << endl;
},
};
}
int main() {
// will print "received 'b' => quit"
spawn(testee);
await_all_actors_done();
// will print "received 'a'" and then "received 'b' => quit"
spawn<priority_aware>(testee);
await_all_actors_done();
shutdown();
}
\end{lstlisting}
\section{Messages}
Messages in \lib are type-erased, copy-on-write tuples.
The actual message type itself is usually hidden, as actors use pattern matching to decompose messages automatically.
However, the classes \lstinline^message^ and \lstinline^message_builder^ allow more advanced usage scenarios than only sending data from one actor to another.
\subsection{Class \texttt{message}}
{\small
\begin{tabular*}{\textwidth}{m{0.45\textwidth}m{0.5\textwidth}}
\multicolumn{2}{m{\linewidth}}{\large{\textbf{Member functions}}\vspace{3pt}} \\
\\
\multicolumn{2}{l}{\textbf{Observers}\vspace{3pt}} \\
\hline
\lstinline^bool empty()^ & Returns whether this message is empty \\
\hline
\lstinline^size_t size()^ & Returns the size of this message \\
\hline
\lstinline^const void* at(size_t p)^ & Returns a const pointer to the element at position \lstinline^p^ \\
\hline
\lstinline^template <class T>^ \lstinline^const T& get_as(size_t p)^ & Returns a const ref. to the element at position \lstinline^p^ \\
\hline
\lstinline^template <class T>^ \lstinline^bool match_element(size_t p)^ & Returns whether the element at position \lstinline^p^ has type \lstinline^T^ \\
\hline
\lstinline^template <class... Ts>^ \lstinline^bool match_elements()^ & Returns whether this message has the types \lstinline^Ts...^ \\
\hline
\lstinline^message drop(size_t n)^ & Creates a new message with all but the first \lstinline^n^ values \\
\hline
\lstinline^message drop_right(size_t n)^ & Creates a new message with all but the last \lstinline^n^ values \\
\hline
\lstinline^message take(size_t n)^ & Creates a new message from the first \lstinline^n^ values \\
\hline
\lstinline^message take_right(size_t n)^ & Creates a new message from the last \lstinline^n^ values \\
\hline
\lstinline^message slice(size_t p, size_t n)^ & Creates a new message from \lstinline^[p, p + n)^ \\
\hline
\lstinline^message slice(size_t p, size_t n)^ & Creates a new message from \lstinline^[p, p + n)^ \\
\hline
\lstinline^message extract(message_handler)^ & See \S\,\ref{Sec::Messages::Extract} \\
\hline
\lstinline^message extract_opts(...)^ & See \S\,\ref{Sec::Messages::ExtractOpts} \\
\hline
\\
\multicolumn{2}{l}{\textbf{Modifiers}\vspace{3pt}} \\
\hline
\lstinline^optional<message>^ \lstinline^apply(message_handler f)^ & Returns \lstinline^f(*this)^ \\
\hline
\lstinline^void* mutable_at(size_t p)^ & Returns a pointer to the element at position p \\
\hline
\lstinline^template <class T>^ \lstinline^T& get_as_mutable(size_t p)^ & Returns a reference to the element at position p \\
\hline
\end{tabular*}
}
\clearpage
\subsection{Class \texttt{message\_builder}}
{\small
\begin{tabular*}{\textwidth}{m{0.45\textwidth}m{0.5\textwidth}}
\multicolumn{2}{m{\linewidth}}{\large{\textbf{Member functions}}\vspace{3pt}} \\
\\
\multicolumn{2}{l}{\textbf{Constructors}\vspace{3pt}} \\
\hline
\lstinline^()^ & Creates an empty message builder \\
\hline
\lstinline^template <class Iter>^ \lstinline^(Iter first, Iter last)^ & Adds all elements from range \lstinline^[first, last)^ \\
\hline
\\
\multicolumn{2}{l}{\textbf{Observers}\vspace{3pt}} \\
\hline
\lstinline^bool empty()^ & Returns whether this message is empty \\
\hline
\lstinline^size_t size()^ & Returns the size of this message \\
\hline
\lstinline^message to_message( )^ & Converts the buffer to an actual message object \\
\hline
\lstinline^template <class T>^ \lstinline^append(T val)^ & Adds \lstinline^val^ to the buffer \\
\hline
\lstinline^template <class Iter>^ \lstinline^append(Iter first, Iter last)^ & Adds all elements from range \lstinline^[first, last)^ \\
\hline
\lstinline^message extract(message_handler)^ & See \S\,\ref{Sec::Messages::Extract} \\
\hline
\lstinline^message extract_opts(...)^ & See \S\,\ref{Sec::Messages::ExtractOpts} \\
\hline
\\
\multicolumn{2}{l}{\textbf{Modifiers}\vspace{3pt}} \\
\hline
\lstinline^optional<message>^ \lstinline^apply(message_handler f)^ & Returns \lstinline^f(*this)^ \\
\hline
\lstinline^message move_to_message()^ & Transfers ownership of its data to the new message \textbf{Warning}: this function leaves the builder in an \emph{invalid state}, i.e., calling any member function on it afterwards is undefined behavior \\
\hline
\end{tabular*}
}
\clearpage
\subsection{Extracting}
\label{Sec::Messages::Extract}
The member function \lstinline^message::extract^ removes matched elements from a message. x
Messages are filtered by repeatedly applying a message handler to the greatest remaining slice, whereas slices are generated in the sequence \lstinline^[0, size)^, \lstinline^[0, size-1)^, \lstinline^...^, \lstinline^[1, size-1)^, \lstinline^...^, \lstinline^[size-1, size)^.
Whenever a slice is matched, it is removed from the message and the next slice starts at the same index on the reduced message.
For example:
\begin{lstlisting}
auto msg = make_message(1, 2.f, 3.f, 4);
// remove float and integer pairs
auto msg2 = msg.extract({
[](float, float) { },
[](int, int) { }
});
assert(msg2 == make_message(1, 4));
\end{lstlisting}
Step-by-step explanation:
\begin{itemize}
\item Slice 1: \lstinline^(1, 2.f, 3.f, 4)^, no match
\item Slice 2: \lstinline^(1, 2.f, 3.f)^, no match
\item Slice 3: \lstinline^(1, 2.f)^, no match
\item Slice 4: \lstinline^(1)^, no match
\item Slice 5: \lstinline^(2.f, 3.f, 4)^, no match
\item Slice 6: \lstinline^(2.f, 3.f)^, \emph{match}; new message is \lstinline^(1, 4)^
\item Slice 7: \lstinline^(4)^, no match
\end{itemize}
Slice 7 is \lstinline^(4)^, i.e., does not contain the first element, because the match on slice 6 occurred at index position 1. The function \lstinline^extract^ iterates a message only once, from left to right.
The returned message contains the remaining, i.e., unmatched, elements.
\clearpage
\subsection{Extracting Command Line Options}
\label{Sec::Messages::ExtractOpts}
The class \lstinline^message^ also contains a convenience interface to \lstinline^extract^ for parsing command line options: the member function \lstinline^extract_opts^.
\begin{lstlisting}
int main(int argc, char** argv) {
uint16_t port;
string host = "localhost";
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"port,p", "set port", port},
{"host,H", "set host (default: localhost)", host},
{"verbose,v", "enable verbose mode"}
});
if (! res.error.empty()) {
// read invalid CLI arguments
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) {
// CLI arguments contained "-h", "--help", or "-?" (builtin);
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// res.remainder stors all extra arguments that weren't consumed
}
if (res.opts.count("verbose") > 0) {
// enable verbose mode
}
// ...
}
/*
Output of ./program_name -h:
Allowed options:
-p [--port] arg : set port
-H [--host] arg : set host (default: localhost)
-v [--verbose] : enable verbose mode
*/
\end{lstlisting}
\section{Network IO}
\label{Sec::NetworkIO}
When communicating to other services in the network, sometimes low-level socket IO is inevitable.
For this reason, \lib provides \emph{brokers}.
A broker is an event-based actor running in the middleman that multiplexes socket IO.
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.
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}
Brokers are spawned using the function \lstinline^spawn_io^ and always use functor-based implementations capturing the self pointer of type \lstinline^broker*^.
For convenience, \lstinline^spawn_io_server^ can be used to spawn a new broker listening to a local port and \lstinline^spawn_io_client^ can be used to spawn a new broker that connects to given host and port or uses existing IO streams.
\begin{lstlisting}
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (broker*)>,
typename... Ts>
actor spawn_io(F fun, Ts&&... args);
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (broker*)>,
typename... Ts>
actor spawn_io_client(F fun,
input_stream_ptr in,
output_stream_ptr out,
Ts&&... args);
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (broker*)>,
typename... Ts>
actor spawn_io_client(F fun, string host, uint16_t port, Ts&&... args);
template<spawn_options Os = no_spawn_options,
typename F = std::function<behavior (broker*)>,
typename... Ts>
actor spawn_io_server(F fun, uint16_t port, Ts&&... args);
\end{lstlisting}
\clearpage
\subsection{Broker Interface}
\label{Sec::NetworkIO::BrokerInterface}
\begin{lstlisting}
class broker;
\end{lstlisting}
{\small
\begin{tabular*}{\textwidth}{m{0.51\textwidth}m{0.44\textwidth}}
\multicolumn{2}{m{\linewidth}}{\large{\textbf{Member Functions}}\vspace{3pt}} \\
\\
\hline
\lstinline^void configure_read(^ \lstinline^connection_handle hdl,^ \lstinline^receive_policy::config config)^ & Modifies the receive policy for the connection identified by \lstinline^hdl^. This will cause the middleman to enqueue the next \lstinline^new_data_msg^ according to the given \lstinline^config^ created by \lstinline^receive_policy::exactly(x)^, \lstinline^receive_policy::at_most(x)^, or \lstinline^receive_policy::at_least(x)^ (with \lstinline^x^ denoting the number of bytes) \\
\hline
\lstinline^void write(connection_handle hdl,^ \lstinline^size_t num_bytes,^ \lstinline^const void* buf)^ & Writes data to the output buffer \\
\hline
\lstinline^void flush(connection_handle hdl)^ & Sends the data from the output buffer \\
\hline
\lstinline^template <class F, class... Ts>^ \lstinline^actor fork(F fun,^ \lstinline^connection_handle hdl, Ts&&... args)^ & Spawns a new broker that takes ownership of given connection \\
\hline
\lstinline^size_t num_connections()^ & Returns the number of open connections \\
\hline
\lstinline^void close(connection_handle hdl)^ & Closes a connection \\
\hline
\lstinline^void close(accept_handle hdl)^ & Closes an acceptor \\
\hline
\end{tabular*}
}
\clearpage
\subsection{Broker-related Message Types}
Brokers receive system messages directly from the middleman whenever an event on one of it handles occurs.
\begin{lstlisting}
struct new_connection_msg {
accept_handle source;
connection_handle handle;
};
\end{lstlisting}
Whenever a new incoming connection (identified by the \lstinline^handle^ field) has been accepted for one of the broker's accept handles, it will receive a \lstinline^new_connection_msg^.
\begin{lstlisting}
struct new_data_msg {
connection_handle handle;
std::vector<char> buf;
};
\end{lstlisting}
New incoming data is transmitted to the broker using messages of type \lstinline^new_data_msg^.
The raw bytes can be accessed as buffer object of type \lstinline^std::vector<char>^.
The amount of data, i.e., how often this message is received, can be controlled using \lstinline^configure_read^ (see \ref{Sec::NetworkIO::BrokerInterface}).
It is worth mentioning that the buffer is re-used whenever possible.
This means, as long as the broker does not create any new references to the message by copying it, the middleman will always use only a single buffer per connection.
\begin{lstlisting}
struct connection_closed_msg {
connection_handle handle;
};
\end{lstlisting}
\begin{lstlisting}
struct acceptor_closed_msg {
accept_handle handle;
};
\end{lstlisting}
A \lstinline^connection_closed_msg^ or \lstinline^ acceptor_closed_msg^ informs the broker that one of it handles is no longer valid.
\section{Network Transparency}
All actor operations as well as sending messages are network transparent.
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}
\begin{lstlisting}
uint16_t publish(actor whom, uint16_t port,
const char* addr = nullptr,
bool reuse_addr = false)
\end{lstlisting}
The function \lstinline^publish^ binds an actor to a given port.
To choose the next high-level port available for binding, one can specify \lstinline^port == 0^ and retrieves the bound port as return value.
The return value is equal to \lstinline^port^ if \lstinline^port != 0^.
The function throws \lstinline^network_error^ if socket related errors occur or \lstinline^bind_failure^ if the specified port is already in use.
The optional \lstinline^addr^ parameter can be used to listen only to the given address.
Otherwise, the actor accepts all incoming connections (\lstinline^INADDR_ANY^).
The flag \lstinline^reuse_addr^ controls the behavior when binding an IP
address to a port, with the same semantics as the BSD socket flag \lstinline^SO_REUSEADDR^.
For example, if \lstinline^reuse_addr = false^, binding two sockets to 0.0.0.0:42 and 10.0.0.1:42 will fail with \texttt{EADDRINUSE} since 0.0.0.0 includes 10.0.0.1.
With \lstinline^reuse_addr = true^ binding would succeed because 10.0.0.1 and
0.0.0.0 are not literally equal addresses.
\begin{lstlisting}
publish(self, 4242);
self->become (
[](ping_atom, int i) {
return std::make_tuple(pong_atom::value, i);
}
);
\end{lstlisting}
To close a socket, e.g., to allow other actors to be published at the port, the function \lstinline^unpublish^ can be used.
This function is called implicitly if a published actor terminates.
\begin{lstlisting}
void unpublish(caf::actor whom, uint16_t port)
\end{lstlisting}
\clearpage
\subsection{Connecting to Remote Actors}
\begin{lstlisting}
actor remote_actor(const char* host, std::uint16_t port)
\end{lstlisting}
The function \lstinline^remote_actor^ connects to the actor at given host and port.
A \lstinline^network_error^ is thrown if the connection failed.
\begin{lstlisting}
auto pong = remote_actor("localhost", 4242);
self->send(pong, ping_atom::value, 0);
self->become (
[=](pong_value, int i) {
if (i >= 10) {
self->quit();
return;
}
self->send(pong, ping_atom::value, i + 1);
}
);
\end{lstlisting}
\section{OpenCL-based Actors}
\lib supports transparent integration of OpenCL functions.
\begin{lstlisting}
// opencl kernel for matrix multiplication;
// last parameter is, by convention, the output parameter
constexpr const char* kernel_source = R"__(
__kernel void matrix_mult(__global float* matrix1,
__global float* matrix2,
__global float* output) {
// we only use square matrices, hence: width == height
size_t size = get_global_size(0); // == get_global_size_(1);
size_t x = get_global_id(0);
size_t y = get_global_id(1);
float result = 0;
for (size_t idx = 0; idx < size; ++idx)
result += matrix1[idx + y * size] * matrix2[x + idx * size];
output[x + y * size] = result;
}
)__";
\end{lstlisting}
This diff is collapsed.
\section{Receiving Messages}
\label{Sec::Receive}
The current \textit{behavior} of an actor is its response to the \textit{next} incoming message and includes (a) sending messages to other actors, (b) creation of more actors, and (c) setting a new behavior.
An event-based actor, i.e., the default implementation in \lib, uses \lstinline^become^ to set its behavior.
The given behavior is then executed until it is replaced by another call to \lstinline^become^ or the actor finishes execution.
\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^make_behavior^ returning the initial behavior.
\begin{lstlisting}
class printer : public event_based_actor {
behavior make_behavior() override {
return {
others >> [] {
cout << to_string(last_dequeued()) << endl;
}
};
}
};
\end{lstlisting}
\clearpage
\begin{lstlisting}
using pop_atom = atom_constant<atom("pop")>;
using push_atom = atom_constant<atom("push")>;
class fixed_stack : public event_based_actor {
public:
fixed_stack(size_t max) : max_size(max) {
full_.assign(
[=](push_atom, int) {
// discard
},
[=](pop_atom) -> message {
auto result = data.back();
data.pop_back();
become(filled_);
return make_message(ok_atom::value, result);
}
);
filled_.assign(
[=](push_atom, int what) {
data.push_back(what);
if (data.size() == max_size) become(full_);
},
[=](pop_atom) -> message {
auto result = data.back();
data.pop_back();
if (data.empty()) become(empty_);
return make_message(ok_atom::value, result);
}
);
empty_.assign(
[=](push_atom, int what) {
data.push_back(what);
become(filled_);
},
[=](pop_atom) {
return error_atom::value;
}
);
}
behavior make_behavior() override {
return empty_;
}
size_t max_size;
std::vector<int> data;
behavior full_;
behavior filled_;
behavior empty_;
};
\end{lstlisting}
\clearpage
\subsection{Nesting Receives Using \lstinline^become/unbecome^}
Since \lstinline^become^ does not block, an actor has to manipulate its behavior stack to achieve nested receive operations.
An actor can set a new behavior by calling \lstinline^become^ with the \lstinline^keep_behavior^ policy to be able to return to its previous behavior later on by calling \lstinline^unbecome^, as shown in the example below.
\begin{lstlisting}
// receives {int, float} sequences
behavior testee(event_based_actor* self) {
return {
[=](int value1) {
self->become (
// the keep_behavior policy stores the current behavior
// on the behavior stack to be able to return to this
// behavior later on by calling unbecome()
keep_behavior,
[=](float value2) {
cout << value1 << " => " << value2 << endl;
// restore previous behavior
self->unbecome();
}
);
}
};
}
\end{lstlisting}
An event-based actor finishes execution with normal exit reason if the behavior stack is empty after calling \lstinline^unbecome^.
The default policy of \lstinline^become^ is \lstinline^discard_behavior^ that causes an actor to override its current behavior.
The policy flag must be the first argument of \lstinline^become^.
\textbf{Note}: the message handling in \lib is consistent among all actor implementations: unmatched messages are \textit{never} implicitly discarded if no suitable handler was found.
Hence, the order of arrival is not important in the example above.
This is unlike other event-based implementations of the actor model such as Akka for instance.
\clearpage
\subsection{Timeouts}
\label{Sec::Receive::Timeouts}
A behavior set by \lstinline^become^ is invoked whenever a new message arrives.
If no message ever arrives, the actor would wait forever.
This might be desirable if the actor only provides a service and should not do anything else.
But often, we need to be able to recover if an expected message does not arrive within a certain time period. The following examples illustrates the usage of \lstinline^after^ to define a timeout.
\begin{lstlisting}
behavior eager_actor(event_based_actor* self) {
return {
[](int i) { /* ... */ },
[](float i) { /* ... */ },
others >> [] { /* ... */ },
after(std::chrono::seconds(10)) >> [] {
aout(self) << "received nothing within 10 seconds..." << endl;
// ...
}
};
}
\end{lstlisting}
Callbacks given as timeout handler must have zero arguments.
Any number of patterns can precede the timeout definition, but ``\lstinline^after^'' must always be the final statement.
Using a zero-duration timeout causes the actor to scan its mailbox once and then invoke the timeout immediately if no matching message was found.
\lib supports timeouts using \lstinline^minutes^, \lstinline^seconds^, \lstinline^milliseconds^ and \lstinline^microseconds^.
However, note that the precision depends on the operating system and your local work load.
Thus, you should not depend on a certain clock resolution.
\clearpage
\subsection{Skipping Messages}
Unmatched messages are skipped automatically by \lib's runtime system.
This is true for \textit{all} actor implementations.
To allow actors to skip messages manually, \lstinline^skip_message^ can be used.
This is in particular useful whenever an actor switches between behaviors, but wants to use a default rule created by \lstinline^others^ to catch messages that are not handled by any of its behaviors.
The following example illustrates a simple server actor that dispatches requests to workers.
After receiving an \lstinline^'idle'^ message, it awaits a request that is then forwarded to the idle worker.
Afterwards, the server returns to its initial behavior, i.e., awaits the next \lstinline^'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}
using idle_atom = atom_constant<atom("idle")>;
using request_atom = atom_constant<atom("request")>;
behavior server(event_based_actor* self) {
auto die = [=] { self->quit(exit_reason::user_defined); };
return {
[=](idle_atom) {
auto worker = last_sender();
self->become (
keep_behavior,
[=](request_atom) {
// forward request to idle worker
self->forward_to(worker);
// await next idle message
self->unbecome();
},
[=](idle_atom) {
return skip_message();
},
others >> die
);
},
[=](request_atom) {
return skip_message();
},
others >> die
};
}
\end{lstlisting}
\section{Sending Messages}
\label{Sec::Send}
\begin{lstlisting}
template<typename... Args>
void send(actor whom, Args&&... what);
\end{lstlisting}
Messages can be sent by using the member function \lstinline^send^.
The variadic template parameter pack \lstinline^what...^ is converted to a message and then enqueued to the mailbox of \lstinline^whom^.
\begin{lstlisting}
void some_fun(event_based_actor* self) {
actor other = spawn(...);
self->send(other, 1, 2, 3);
// sending a message directly is also ok:
auto msg = make_message(1, 2, 3);
self->send(other, msg);
}
\end{lstlisting}
\subsection{Replying to Messages}
\label{Sec::Send::Reply}
The return value of a message handler is used as response message.
Actors can also use the result of a \lstinline^sync_send^ to answer to a request, as shown below.
\begin{lstlisting}
behavior client(event_based_actor* self, const actor& master) {
return {
[=](const string& request) {
return self->sync_send(master, request).then(
[=](const std::string& response) {
return response;
}
);
}
};
};
\end{lstlisting}
\subsection{Delaying Messages}
Messages can be delayed by using the function \lstinline^delayed_send^.
\begin{lstlisting}
using poll_atom = atom_constant<atom("poll")>;
behavior poller(event_based_actor* self) {
using std::chrono::seconds;
self->delayed_send(self, seconds(1), poll_atom::value);
return {
[](poll_atom) {
// poll a resource
// ...
// schedule next polling
self->delayed_send(self, seconds(1), poll_atom::value);
}
};
}
\end{lstlisting}
\clearpage
\subsection{Forwarding Messages in Untyped Actors}
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.
The following diagram illustrates forwarding of a synchronous message from actor \texttt{B} to actor \texttt{C}.
\begin{footnotesize}
\begin{verbatim}
A B C
| | |
| --(sync_send)--> | |
| | --(forward_to)-> |
| X |---\
| | | compute
| | | result
| |<--/
| <-------------(reply)-------------- |
| X
|---\
| | handle
| | response
|<--/
|
X
\end{verbatim}
\end{footnotesize}
The forwarding is completely transparent to actor \texttt{C}, since it will see actor \texttt{A} as sender of the message.
However, actor \texttt{A} will see actor \texttt{C} as sender of the response message instead of actor \texttt{B} and thus could recognize the forwarding by evaluating \lstinline^self->last_sender()^.
\section{Spawning Actors}
Actors are created using the function \lstinline^spawn^.
The easiest way to implement actors is to use functors, e.g., a free function or a lambda expression.
The arguments to the functor are passed to \lstinline^spawn^ as additional arguments.
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 flag \lstinline^detached^ causes \lstinline^spawn^ to assign a dedicated thread to the actor, i.e., to opt-out of the cooperative scheduling.
Convenience flags like \lstinline^linked^ or \lstinline^monitored^ automatically link or monitor to the newly created actor.
Naturally, these two flags are not available on ``top-level'' spawns.
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.
\begin{lstlisting}
#include "caf/all.hpp"
using namespace caf;
void my_actor1();
void my_actor2(event_based_actor*, int arg1, const std::string& arg2);
void ugly_duckling(blocking_actor*);
class my_actor3 : public event_based_actor { /* ... */ };
class my_actor4 : public event_based_actor {
public: my_actor4(int some_value) { /* ... */ }
/* ... */
};
// whenever we want to link to or monitor a spawned actor,
// we have to spawn it using the self pointer, otherwise
// we can use the free function 'spawn' (top-level spawn)
void server(event_based_actor* self) {
// spawn functor-based actors
auto a0 = self->spawn(my_actor1);
auto a1 = self->spawn<linked>(my_actor2, 42, "hello actor");
auto a2 = self->spawn<monitored>([] { /* ... */ });
auto a3 = self->spawn([](int) { /* ... */ }, 42);
// spawn thread-mapped actors
auto a4 = self->spawn<detached>(my_actor1);
auto a5 = self->spawn<detached + linked>([] { /* ... */ });
auto a6 = self->spawn<detached>(my_actor2, 0, "zero");
// spawn class-based actors
auto a7 = self->spawn<my_actor3>();
auto a8 = self->spawn<my_actor4, monitored>(42);
// spawn and detach class-based actors
auto a9 = self->spawn<my_actor4, detached>(42);
// spawn actors that need access to the blocking API
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 the `blocking_api` flag
// --- auto ab = self->spawn<blocking_api>(my_actor2);
}
\end{lstlisting}
%TODO: check how std::bind(..., self) behaves atm
%\textbf{Note}: \lstinline^spawn(fun, arg0, ...)^ is \textbf{not} equal to \lstinline^spawn(std::bind(fun, arg0, ...))^!
%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.
\section{Strongly Typed Actors}
Strongly typed actors provide a convenient way of defining type-safe messaging interfaces.
Unlike untyped actorsd, typed actors are not allowed to use guard expressions.
When calling \lstinline^become^ in a strongly typed actor, \emph{all} message handlers from the typed interface must be set.
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.
\subsection{Spawning Typed Actors}
\label{sec:strong:spawn}
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 \lib needs to evaluate the signature of each message handler.
\begin{lstlisting}
auto p0 = spawn_typed(
[](int a, int b) {
return static_cast<double>(a) * b;
},
[](double a, double b) {
return std::make_tuple(a * b, a / b);
}
);
// assign to identical type
using full_type = typed_actor<
replies_to<int, int>::with<double>,
replies_to<double, double>::with<double, double>
>;
full_type p1 = p0;
// assign to subtype
using subtype1 = typed_actor<
replies_to<int, int>::with<double>
>;
subtype1 p2 = p0;
// assign to another subtype
using subtype2 = typed_actor<
replies_to<double, double>::with<double, double>
>;
subtype2 p3 = p0;
\end{lstlisting}
\clearpage
\subsection{Class-based Typed Actors}
Typed actors are spawned using the function \lstinline^spawn_typed^ and define their message passing interface as list of \lstinline^replies_to<...>::with<...>^ statements.
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}
struct shutdown_request { };
struct plus_request { int a; int b; };
struct minus_request { int a; int b; };
typedef typed_actor<replies_to<plus_request>::with<int>,
replies_to<minus_request>::with<int>,
replies_to<shutdown_request>::with<void>>
calculator_type;
calculator_type::behavior_type
typed_calculator(calculator_type::pointer self) {
return {
[](const plus_request& pr) {
return pr.a + pr.b;
},
[](const minus_request& pr) {
return pr.a - pr.b;
},
[=](const shutdown_request&) {
self->quit();
}
};
}
class typed_calculator_class : public calculator_type::base {
protected: behavior_type make_behavior() override {
return {
[](const plus_request& pr) {
return pr.a + pr.b;
},
[](const minus_request& pr) {
return pr.a - pr.b;
},
[=](const shutdown_request&) {
quit();
}
};
}
};
\end{lstlisting}
\clearpage
\begin{lstlisting}
void tester(event_based_actor* self, const calculator_type& testee) {
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, plus_request{2, 1}).then(
[=](int r1) {
assert(r1 == 3);
// second test: 2 - 1 = 1
self->sync_send(testee, minus_request{2, 1}).then(
[=](int r2) {
assert(r2 == 1);
// both tests succeeded
aout(self) << "AUT (actor under test) "
<< "seems to be ok"
<< endl;
self->send(testee, shutdown_request{});
}
);
}
);
}
int main() {
// announce custom message types
announce<shutdown_request>("shutdown_request");
announce<plus_request>("plus_request",
&plus_request::a, &plus_request::b);
announce<minus_request>("minus_request",
&minus_request::a, &minus_request::b);
// test function-based impl
spawn(tester, spawn_typed(typed_calculator));
await_all_actors_done();
// test class-based impl
spawn(tester, spawn_typed<typed_calculator_class>());
await_all_actors_done();
// done
shutdown();
return 0;
}
\end{lstlisting}
\section{Synchronous Communication}
\label{Sec::Sync}
\lib supports both asynchronous and synchronous communication. The latter is provided by the member function \lstinline^sync_send^.
\begin{lstlisting}
template<typename... Args>
__unspecified__ sync_send(actor whom, Args&&... what);
\end{lstlisting}
A synchronous message is sent to the receiving actor's mailbox like any other (asynchronous) message. Only the response message is treated separately.
\subsection{Additional Error Messages}
\begin{lstlisting}
struct sync_exited_msg {
actor_addr source;
uint32_t reason;
};
\end{lstlisting}
When using synchronous messaging, \lib's runtime will send a \lstinline^sync_exited_msg^ message if the receiver is not alive. This is in addition to exit and down messages caused by linking or monitoring.
\subsection{Receive Response Messages}
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}
void foo(event_based_actor* self, actor testee) {
// testee replies with a string to 'get'
self->sync_send(testee, get_atom::value).then(
[=](const std::string& str) {
// handle str
},
after(std::chrono::seconds(30)) >> [=]() {
// handle error
}
);
);
\end{lstlisting}
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 to the previous behavior afterwards.
\clearpage
\subsection{Synchronous Failures and Error Handlers}
An unexpected response message, i.e., a message that is not handled by the ``one-shot-handler'', is considered as an error. The runtime will invoke the actor's \lstinline^on_sync_failure^, which kills the actor by calling \lstinline^self->quit(exit_reason::unhandled_sync_failure)^ per default. The error handler can be overridden by calling \lstinline^self->on_sync_failure(...)^ as shown below.
\begin{lstlisting}
void foo(event_based_actor* self, actor testee) {
// set handler for unexpected messages
self->on_sync_failure([=] {
aout(self) << "received unexpected synchronous response: "
<< to_string(self->last_dequeued()) << endl;
});
// set response handler by using "then"
sync_send(testee, get_atom::value).then(
[=](const std::string& str) {
/* handle str */
}
// any other result will call the on_sync_failure handler
);
}
\end{lstlisting}
\section{Platform-Independent Type System}
\label{Sec::TypeSystem}
\lib provides a fully network transparent communication between actors.
Thus, \lib needs to serialize and deserialize messages.
Unfortunately, this is not possible using the RTTI system of C++.
\lib uses its own RTTI based on the class \lstinline^uniform_type_info^, since it is not possible to extend \lstinline^std::type_info^.
Unlike \lstinline^std::type_info::name()^, \lstinline^uniform_type_info::name()^ is guaranteed to return the same name on all supported platforms. Furthermore, it allows to create an instance of a type by name.
\begin{lstlisting}
// creates a signed, 32 bit integer
uniform_value i = uniform_typeid<int>()->create();
\end{lstlisting}
You should rarely, if ever, need to use \lstinline^uniform_value^ or \lstinline^uniform_type_info^.
The type \lstinline^uniform_value^ stores a type-erased pointer along with the associated \lstinline^uniform_type_info^.
The sole purpose of this simple abstraction is to enable the pattern matching engine of \lib to query the type information and then dispatch the value to a message handler.
When using a \lstinline^message_builder^, each element is stored as a \lstinline^uniform_value^.
\subsection{User-Defined Data Types in Messages}
\label{Sec::TypeSystem::UserDefined}
All user-defined types must be explicitly ``announced'' so that \lib can (de)serialize them correctly, as shown in the example below.
\begin{lstlisting}
#include "caf/all.hpp"
struct foo { int a; int b; };
int main() {
caf::announce<foo>("foo", &foo::a, &foo::b);
// ... foo can now safely be used in messages ...
}
\end{lstlisting}
Without announcing \lstinline^foo^, \lib is not able to (de)serialize instances of it.
The function \lstinline^announce()^ takes the class as template parameter.
The first argument to the function always is the type name followed by pointers to all members (or getter/setter pairs).
This works for all primitive data types and STL compliant containers.
See the announce examples 1 -- 4 of the standard distribution for more details.
Obviously, there are limitations.
You have to implement serialize/deserialize by yourself if your class does implement an unsupported data structure.
See \lstinline^announce_example_5.cpp^ in the examples folder.
HEVEA=hevea
HEVEA_ARGS=-I ..
all:
$(HEVEA) $(HEVEA_ARGS) manual.tex
$(HEVEA) $(HEVEA_ARGS) manual.tex
$(HEVEA) $(HEVEA_ARGS) manual.tex
clean:
rm -f *.haux *.html *.htoc
.PHONY: all clean
PDFLATEX="pdflatex"
all:
@TEXINPUTS="..:" $(PDFLATEX) manual.tex
@TEXINPUTS="..:" $(PDFLATEX) manual.tex
@TEXINPUTS="..:" $(PDFLATEX) manual.tex
clean:
rm -f *.aux *.toc *.pdf *.log *.out
.PHONY: all clean
%%
%% This is file `cmbright.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% cmbright.dtx (with options: `package')
%%
%% IMPORTANT NOTICE:
%%
%% For the copyright see the source file.
%%
%% Any modified versions of this file must be renamed
%% with new filenames distinct from cmbright.sty.
%%
%% For distribution of the original source see the terms
%% for copying and modification in the file cmbright.dtx.
%%
%% This generated file may be distributed as long as the
%% original source files, as listed above, are part of the
%% same distribution. (The sources need not necessarily be
%% in the same archive or directory.)
\ProvidesPackage{cmbright}
[2005/04/13 v8.1 (WaS)]
\NeedsTeXFormat{LaTeX2e}[1995/06/01]
\renewcommand{\familydefault}{\sfdefault}
\renewcommand{\sfdefault}{cmbr}
\renewcommand{\ttdefault}{cmtl}
\DeclareSymbolFont {operators} {OT1}{cmbr}{m}{n}
\DeclareSymbolFont {letters} {OML}{cmbrm}{m}{it}
\SetSymbolFont {letters}{bold} {OML}{cmbrm}{b}{it}
\DeclareSymbolFont {symbols} {OMS}{cmbrs}{m}{n}
\DeclareMathAlphabet{\mathit} {OT1}{cmbr}{m}{sl}
\DeclareMathAlphabet{\mathbf} {OT1}{cmbr}{bx}{n}
\DeclareMathAlphabet{\mathtt} {OT1}{cmtl}{m}{n}
\DeclareMathAlphabet{\mathbold}{OML}{cmbrm}{b}{it}
\DeclareMathSymbol{\alpha}{\mathalpha}{letters}{11}
\DeclareMathSymbol{\beta}{\mathalpha}{letters}{12}
\DeclareMathSymbol{\gamma}{\mathalpha}{letters}{13}
\DeclareMathSymbol{\delta}{\mathalpha}{letters}{14}
\DeclareMathSymbol{\epsilon}{\mathalpha}{letters}{15}
\DeclareMathSymbol{\zeta}{\mathalpha}{letters}{16}
\DeclareMathSymbol{\eta}{\mathalpha}{letters}{17}
\DeclareMathSymbol{\theta}{\mathalpha}{letters}{18}
\DeclareMathSymbol{\iota}{\mathalpha}{letters}{19}
\DeclareMathSymbol{\kappa}{\mathalpha}{letters}{20}
\DeclareMathSymbol{\lambda}{\mathalpha}{letters}{21}
\DeclareMathSymbol{\mu}{\mathalpha}{letters}{22}
\DeclareMathSymbol{\nu}{\mathalpha}{letters}{23}
\DeclareMathSymbol{\xi}{\mathalpha}{letters}{24}
\DeclareMathSymbol{\pi}{\mathalpha}{letters}{25}
\DeclareMathSymbol{\rho}{\mathalpha}{letters}{26}
\DeclareMathSymbol{\sigma}{\mathalpha}{letters}{27}
\DeclareMathSymbol{\tau}{\mathalpha}{letters}{28}
\DeclareMathSymbol{\upsilon}{\mathalpha}{letters}{29}
\DeclareMathSymbol{\phi}{\mathalpha}{letters}{30}
\DeclareMathSymbol{\chi}{\mathalpha}{letters}{31}
\DeclareMathSymbol{\psi}{\mathalpha}{letters}{32}
\DeclareMathSymbol{\omega}{\mathalpha}{letters}{33}
\DeclareMathSymbol{\varepsilon}{\mathalpha}{letters}{34}
\DeclareMathSymbol{\vartheta}{\mathalpha}{letters}{35}
\DeclareMathSymbol{\varpi}{\mathalpha}{letters}{36}
\DeclareMathSymbol{\varrho}{\mathalpha}{letters}{37}
\DeclareMathSymbol{\varsigma}{\mathalpha}{letters}{38}
\DeclareMathSymbol{\varphi}{\mathalpha}{letters}{39}
\DeclareOption{slantedGreek}{%
\DeclareMathSymbol{\Gamma}{\mathalpha}{letters}{0}
\DeclareMathSymbol{\Delta}{\mathalpha}{letters}{1}
\DeclareMathSymbol{\Theta}{\mathalpha}{letters}{2}
\DeclareMathSymbol{\Lambda}{\mathalpha}{letters}{3}
\DeclareMathSymbol{\Xi}{\mathalpha}{letters}{4}
\DeclareMathSymbol{\Pi}{\mathalpha}{letters}{5}
\DeclareMathSymbol{\Sigma}{\mathalpha}{letters}{6}
\DeclareMathSymbol{\Upsilon}{\mathalpha}{letters}{7}
\DeclareMathSymbol{\Phi}{\mathalpha}{letters}{8}
\DeclareMathSymbol{\Psi}{\mathalpha}{letters}{9}
\DeclareMathSymbol{\Omega}{\mathalpha}{letters}{10}
}
\let\upDelta\Delta
\let\upOmega\Omega
\let\upGamma\Gamma
\let\upTheta\Theta
\let\upLambda\Lambda
\let\upXi\Xi
\let\upPi\Pi
\let\upSigma\Sigma
\let\upUpsilon\Upsilon
\let\upPhi\Phi
\let\upPsi\Psi
\def\bright@baselineskip@table
{<\@viiipt>10<\@ixpt>11.25<\@xpt>12.5<\@xipt>13.7<\@xiipt>15}
\def\bright@setfontsize#1#2#3%
{\edef\@tempa{\def\noexpand\@tempb####1<#2}%
\@tempa>##2<##3\@nil{\def\bright@baselineskip@value{##2}}%
\edef\@tempa{\noexpand\@tempb\bright@baselineskip@table<#2}%
\@tempa><\@nil
\ifx\bright@baselineskip@value\@empty
\def\bright@baselineskip@value{#3}%
\fi
\old@setfontsize{#1}{#2}\bright@baselineskip@value}
\let\old@setfontsize=\@setfontsize
\DeclareOption{enlarged-baselineskips}{%
\let\@setfontsize=\bright@setfontsize}
\DeclareOption{standard-baselineskips}{%
\let\@setfontsize=\old@setfontsize}
\DeclareTextCommand{\textsterling}{OT1}{{%
\ifdim \fontdimen\@ne\font >\z@
\fontfamily{\rmdefault}\fontshape{it}\selectfont
\else
\fontfamily{\rmdefault}\fontshape{ui}\selectfont
\fi
\char`\$}}
\def\mathsterling{\textsl{\textsterling}}
\AtBeginDocument{%
\DeclareFontFamily{U}{msa}{}
\DeclareFontShape{U}{msa}{m}{n}{%
<-9>cmbras8%
<9-10>cmbras9%
<10->cmbras10%
}{}
\DeclareFontFamily{U}{msb}{}
\DeclareFontShape{U}{msb}{m}{n}{%
<-9>cmbrbs8%
<9-10>cmbrbs9%
<10->cmbrbs10%
}{}
}
\def\TeX{T\kern-.19em\lower.5ex\hbox{E}\kern-.05emX\@}
\DeclareRobustCommand{\LaTeX}{L\kern-.3em%
{\sbox\z@ T%
\vbox to\ht\z@{\hbox{\check@mathfonts
\fontsize\sf@size\z@
\math@fontsfalse\selectfont
A}%
\vss}%
}%
\kern-.15em%
\TeX}
\DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
\if b\expandafter\@car\f@series\@nil\boldmath\fi
\LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
\ExecuteOptions{enlarged-baselineskips}
\ProcessOptions\relax
\normalfont\normalsize
\endinput
%%
%% End of file `cmbright.sty'.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Comment.sty version 3.0, 3 September 1992
% selectively in/exclude pieces of text: the user can define new
% comment versions, and each is controlled separately.
% Special comments can be defined where the user specifies the
% action that is to be taken with each comment line.
%
% This style can be used with plain TeX or LaTeX, and probably
% most other packages too.
%
% Author
% Victor Eijkhout
% Department of Computer Science
% University Tennessee at Knoxville
% 104 Ayres Hall
% Knoxville, TN 37996
% USA
%
% eijkhout@cs.utk.edu
%
% Usage: all text included in between
% \comment ... \endcomment
% or \begin{comment} ... \end{comment}
% is discarded. The closing command should appear on a line
% of its own. No starting spaces, nothing after it.
% This environment should work with arbitrary amounts
% of comment.
%
% Other 'comment' environments are defined by
% and are selected/deselected with
% \includecomment{versiona}
% \excludecoment{versionb}
%
% These environments are used as
% \versiona ... \endversiona
% or \begin{versiona} ... \end{versiona}
% with the closing command again on a line of its own.
%
% Special comments are defined as
% \specialcomment{name}{before commands}{after commands}
% where the second and third arguments are executed before
% and after each comment. By defining a control sequence
% \Thiscomment##1{...} in the before commands the user can
% specify what is to be done which each comment line.
%
% Basic approach:
% to comment something out, scoop up every line in verbatim mode
% as macro argument, then throw it away.
% For inclusions, both the opening and closing comands
% are defined as noop
\def\makeinnocent#1{\catcode`#1=12 }
\def\csarg#1#2{\expandafter#1\csname#2\endcsname}
\def\TreatAsComment#1{\begingroup
\def\CurrentComment{#1}%
\let\do\makeinnocent \dospecials
\makeinnocent\^^L% and whatever other special cases
\endlinechar`\^^M \catcode`\^^M=12 \xComment}
{\catcode`\^^M=12 \endlinechar=-1 %
\gdef\xComment#1^^M{\def\test{#1}
\csarg\ifx{PlainEnd\CurrentComment Test}\test
\def\next{\endgroup\AfterComment}%
\else \csarg\ifx{LolliEnd\CurrentComment Test}\test
\def\next{\endgroup\AfterComment}%
\else \csarg\ifx{LaLaEnd\CurrentComment Test}\test
\edef\next{\endgroup\noexpand\AfterComment
\noexpand\end{\CurrentComment}}
\else \ThisComment{#1}\let\next\xComment
\fi \fi \fi \next}
}
\def\includecomment
#1{\message{Including comment '#1'}%
\expandafter\def\csname#1\endcsname{}%
\expandafter\def\csname end#1\endcsname{}}
\def\excludecomment
#1{\message{Excluding comment '#1'}%
\csarg\def{#1}{\let\AfterComment\relax
\def\ThisComment####1{}\TreatAsComment{#1}}%
{\escapechar=-1\relax
\csarg\xdef{PlainEnd#1Test}{\string\\end#1}%
\csarg\xdef{LolliEnd#1Test}{\string\\#1Stop}%
\csarg\xdef{LaLaEnd#1Test}{\string\\end\string\{#1\string\}}%
}}
\long\def\specialcomment
#1#2#3{\message{Special comment '#1'}%
\csarg\def{#1}{\def\ThisComment{}\def\AfterComment{#3}#2%
\TreatAsComment{#1}}%
{\escapechar=-1\relax
\csarg\xdef{PlainEnd#1Test}{\string\\end#1}%
\csarg\xdef{LolliEnd#1Test}{\string\\#1Stop}%
\csarg\xdef{LaLaEnd#1Test}{\string\\end\string\{#1\string\}}%
}}
\excludecomment{comment}
\endinput
% hevea : hevea.sty
% This is a very basic style file for latex document to be processed
% with hevea. It contains definitions of LaTeX environment which are
% processed in a special way by the translator.
% Mostly :
% - latexonly, not processed by hevea, processed by latex.
% - htmlonly , the reverse.
% - rawhtml, to include raw HTML in hevea output.
% - toimage, to send text to the image file.
% The package also provides hevea logos, html related commands (ahref
% etc.), void cutting and image commands.
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{hevea}[2002/01/11]
\RequirePackage{comment}
\newif\ifhevea\heveafalse
\@ifundefined{ifimagen}{\newif\ifimagen\imagenfalse}
\makeatletter%
\newcommand{\heveasmup}[2]{%
\raise #1\hbox{$\m@th$%
\csname S@\f@size\endcsname
\fontsize\sf@size 0%
\math@fontsfalse\selectfont
#2%
}}%
\DeclareRobustCommand{\hevea}{H\kern-.15em\heveasmup{.2ex}{E}\kern-.15emV\kern-.15em\heveasmup{.2ex}{E}\kern-.15emA}%
\DeclareRobustCommand{\hacha}{H\kern-.15em\heveasmup{.2ex}{A}\kern-.15emC\kern-.1em\heveasmup{.2ex}{H}\kern-.15emA}%
\DeclareRobustCommand{\html}{\protect\heveasmup{0.ex}{HTML}}
%%%%%%%%% Hyperlinks hevea style
\newcommand{\ahref}[2]{{#2}}
\newcommand{\ahrefloc}[2]{{#2}}
\newcommand{\aname}[2]{{#2}}
\newcommand{\ahrefurl}[1]{\texttt{#1}}
\newcommand{\footahref}[2]{#2\footnote{\texttt{#1}}}
\newcommand{\mailto}[1]{\texttt{#1}}
\newcommand{\imgsrc}[2][]{}
\newcommand{\home}[1]{\protect\raisebox{-.75ex}{\char126}#1}
\AtBeginDocument
{\@ifundefined{url}
{%url package is not loaded
\let\url\ahref\let\oneurl\ahrefurl\let\footurl\footahref}
{}}
%% Void cutting instructions
\newcounter{cuttingdepth}
\newcommand{\tocnumber}{}
\newcommand{\notocnumber}{}
\newcommand{\cuttingunit}{}
\newcommand{\cutdef}[2][]{}
\newcommand{\cuthere}[2]{}
\newcommand{\cutend}{}
\newcommand{\htmlhead}[1]{}
\newcommand{\htmlfoot}[1]{}
\newcommand{\htmlprefix}[1]{}
\newenvironment{cutflow}[1]{}{}
\newcommand{\cutname}[1]{}
\newcommand{\toplinks}[3]{}
\newcommand{\setlinkstext}[3]{}
\newcommand{\flushdef}[1]{}
\newcommand{\footnoteflush}[1]{}
%%%% Html only
\excludecomment{rawhtml}
\newcommand{\rawhtmlinput}[1]{}
\excludecomment{htmlonly}
%%%% Latex only
\newenvironment{latexonly}{}{}
\newenvironment{verblatex}{}{}
%%%% Image file stuff
\def\toimage{\endgroup}
\def\endtoimage{\begingroup\def\@currenvir{toimage}}
\def\verbimage{\endgroup}
\def\endverbimage{\begingroup\def\@currenvir{verbimage}}
\newcommand{\imageflush}[1][]{}
%%% Bgcolor definition
\newsavebox{\@bgcolorbin}
\newenvironment{bgcolor}[2][]
{\newcommand{\@mycolor}{#2}\begin{lrbox}{\@bgcolorbin}\vbox\bgroup}
{\egroup\end{lrbox}%
\begin{flushleft}%
\colorbox{\@mycolor}{\usebox{\@bgcolorbin}}%
\end{flushleft}}
%%% Style sheets macros, defined as no-ops
\newcommand{\newstyle}[2]{}
\newcommand{\addstyle}[1]{}
\newcommand{\setenvclass}[2]{}
\newcommand{\getenvclass}[1]{}
\newcommand{\loadcssfile}[1]{}
\newenvironment{divstyle}[1]{}{}
\newenvironment{cellstyle}[2]{}{}
\newif\ifexternalcss
%%% Postlude
\makeatother
\documentclass[%
a4paper,% % DIN A4
oneside,% % einseitiger Druck
12pt,% % 12pt Schriftgröße
]{article}
\usepackage[utf8]{inputenc}
% include required packages
\usepackage{url}
\usepackage{array}
\usepackage{color}
\usepackage{listings}
\usepackage{xspace}
\usepackage{tabularx}
\usepackage{hyperref}
\usepackage{fancyhdr}
\usepackage{multicol}
% HTML compilation
\usepackage{hevea}
% font setup
\usepackage{cmbright}
\usepackage{txfonts}
\usepackage[scaled=.90]{helvet}
\usepackage{courier}
% par. settings
\parindent 0pt
\parskip 8pt
\pretolerance=150
\tolerance=500
\hbadness=752
\definecolor{blue}{rgb}{0,0,1}
\definecolor{violet}{rgb}{0.5,0,0.5}
\definecolor{darkred}{rgb}{0.5,0,0}
\definecolor{darkblue}{rgb}{0,0,0.5}
\definecolor{darkgreen}{rgb}{0,0.5,0}
\pagenumbering{arabic}
\input{variables}
\title{%
%BEGIN LATEX
\libtitle
%END LATEX
\libsubtitle}
\author{Dominik Charousset}
\date{\today}
% page setup
\setlength{\voffset}{-0.5in}
\setlength{\hoffset}{-0.5in}
\addtolength{\textwidth}{1in}
\addtolength{\textheight}{1.5in}
\setlength{\headheight}{15pt}
% some HEVEA / HTML style sheets
\newstyle{body}{width:600px;margin:auto;padding-top:20px;text-align: justify;}
% more compact itemize
\newenvironment{itemize*}%
{\begin{itemize}%
\setlength{\itemsep}{0pt}%
\setlength{\parskip}{0pt}}%
{\end{itemize}}
\begin{document}
% fancy header setup
%BEGIN LATEX
\fancypagestyle{plain}{%
\fancyhf{} % clear all header and footer fields
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0pt}
}
%END LATEX
\maketitle
\clearpage
\pagestyle{empty}
\tableofcontents
\clearpage
% fancy header setup for page 1
%BEGIN LATEX
\setcounter{page}{1}
\pagenumbering{arabic}
\pagestyle{fancy}
\fancyhead[L,R]{}
\fancyhead[C]{\leftmark}
\renewcommand{\sectionmark}[1]{%
\markboth{\MakeUppercase{#1}}{}%
}
%END LATEX
% code listings setup
\lstset{%
language=C++,%
morekeywords={constexpr,nullptr,size_t,uint32_t,assert,override,final},%
basicstyle=\ttfamily\small,%
sensitive=true,%
keywordstyle=\color{blue},%
stringstyle=\color{darkgreen},%
commentstyle=\color{violet},%
showstringspaces=false,%
tabsize=4,%
numberstyle=\footnotesize%
}
% content
\include{Introduction}
\include{FirstSteps}
\include{PatternMatching}
\include{Actors}
\include{SendingMessages}
\include{ReceivingMessages}
\include{SynchronousMessages}
\include{ActorManagement}
\include{SpawningActors}
\include{MessagePriorities}
\include{NetworkTransparency}
\include{NetworkIO}
\include{GroupCommunication}
\include{ManagingGroupsOfWorkers}
\include{ActorCompanions}
\include{TypeSystem}
\include{BlockingAPI}
\include{StronglyTypedActors}
\include{Messages}
\include{CommonPitfalls}
\include{Appendix}
\end{document}
\newcommand{\lib}{CAF\xspace}
\newcommand{\libtitle}{%
\texttt{\huge{\textbf{\lib}}}\\~\\A C++ framework for actor programming\\~\\~\\~\\%
}
\newcommand{\libsubtitle}{%
User Manual\\\normalsize{\lib version @CAF_VERSION@}\vfill%
}
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