libcppa |
To compile libcppa, you will need CMake, the Boost Library and a C++11 compiler. To get and compile the sources, open a terminal and type:
git clone git://github.com/Neverlord/libcppa.git cd libcppa ./configure make make install [as root, optional]
It is recommended to run the unit tests as well:
make test
Please submit a bug report that includes (a) your compiler version, (b) your OS, and (c) the content of the file build/Testing/Temporary/LastTest.log if an error occurs.
The message passing implementation of libcppa uses tuples with call-by-value semantic. Hence, it is not necessary to declare message types, though, libcppa allows users to use user-defined types in messages (see Section 13.1). A call-by-value semantic would cause multiple copies of a tuple if it is send to multiple actors. To avoid unnecessary copying overhead, libcppa 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 exist. Thus, race conditions cannot occur and each tuple is copied only if necessary.
The interface of cow_tuple strictly distinguishes between const and non-const access.
The template function get returns an element as immutable value, while 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 get, because this would cause to unintended, and thus unnecessary, copying overhead.
The class 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 tuple_cast.
Users of libcppa usually do not need to know about
any_tuple, since it is used “behind the scenes”.
However, any_tuple can be created from a cow_tuple
or by using make_any_tuple, as shown below.
The function tuple_cast restores static type information
from an any_tuple object.
It returns an option (see Section 14.1)
for a cow_tuple of the requested types.
The function tuple_cast can be used with wildcards (see Section 3.4) to create a view to a subset of the original data.
No elements are copied, unless the tuple becomes detached.
C++ does not provide pattern matching facilities. A general pattern matching solution for arbitrary data structures would require a language extension. Hence, we decided to restrict our implementation to tuples, to be able to use an internal domain-specific language approach.
A match expression begins with a call to the function on, which returns an intermediate object providing the member function when and 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 on,
as shown in the example below.
The result of operator>> is a partial function that is defined for the types given to on.
A comma separated list of partial functions results in a single partial function that sequentially evaluates its subfunctions.
At most one callback is invoked, since the evaluation stops at the first match.
Note: A list of partial function definitions must be enclosed in brackets if assigned to a variable. Otherwise, the compiler assumes commas to separate variable definitions.
The function “on” can be used in two ways.
Either with template parameters only or with function parameters only.
The latter version deduces all types from its arguments and matches for both type and value.
The template “val” can be used to match only the type of a parameter.
Note: The given callback can have less arguments than given to the pattern. But it is only allowed to skip arguments from left to right.
Assume an actor provides a mathematical service for integers.
It takes two arguments, performs a predefined operation and returns the result.
It cannot determine an operation, such as multiply or add, by receiving two operands.
Thus, the operation must be encoded into the message.
The Erlang programming language introduced an approach to use non-numerical
constants, so-called atoms, which have an unambiguous, special-purpose type and do not have the runtime overhead of string constants.
Atoms are mapped to integer values at compile time in libcppa.
This mapping is guaranteed to be collision-free but limits atom literals to ten characters and prohibits special characters.
Legal characters are “_0-9A-Za-z” and the whitespace character.
Atoms are created using the constexpr function atom, as the following example illustrates.
Note: The current implementation cannot enforce the restrictions at compile time, except for a length check.
Each invalid character is mapped to the whitespace character, why the assertion atom("!?") != atom("?!") is not true.
However, this issue will fade away after user-defined literals become available in mainstream compilers, because it is then possible to raise a compiler error for invalid characters.
arg_match” and “on_arg_match”Our previous example is quite verbose and redundant, since you have to type the types twice – as template parameter and as argument type for the lambda.
To avoid such redundancy, arg_match can be used as last argument to the function on.
This causes the compiler to deduce all further types from the signature of the given callback.
Note that the second version does call on without template parameters.
Furthermore, arg_match must be passed as last parameter.
If all types should be deduced from the callback signature, on_arg_match can be used.
It is equal to on(arg_match).
The type anything can be used as wildcard to match any number of any types.
A pattern created by on<anything>() or its alias others() is useful to define a default case.
For patterns defined without template parameters, the constexpr value any_vals can be used as function argument.
The constant any_vals is of type anything and is nothing but syntactic sugar for defining patterns.
Guards can be used to constrain a given match statement by using placeholders, as the following example illustrates.
Guard expressions are a lazy evaluation technique.
The placeholder _x1 is substituted with the first value of a given tuple.
All binary comparison and arithmetic operators are supported, as well as && and ||.
In addition, there are two functions designed to be used in guard expressions: gref (“guard reference”) and gcall (“guard function call”).
The function gref creates a reference wrapper.
It is similar to std::ref but it is always const and “lazy”, i.e., evaluated when a tuple arrives.
A few examples to illustrate some pitfalls:
Statement (5) is evaluated immediately and returns a boolean, whereas statement (4) creates a valid guard expression.
Thus, you should always use gref instead of std::ref to avoid errors.
The second function, gcall, encapsulates a function call.
Its usage is similar to std::bind, but there is also a short version for unary functions: gcall(fun, _x1) is equal to _x1(fun).
Member functions (x represents the value at runtime, y represents an iterable container) | |
size() | Returns x.size() |
empty() | Returns x.empty() |
not_empty() | Returns !x.empty() |
front() | Returns an option (see Section 14.1) to x.front() |
in(y) | Returns true if y contains x, false otherwise |
not_in(y) | Returns !in(y) |
Projections perform type conversions or extract data from a given input. If a callback expects an integer but the received message contains a string, a projection can be used to perform a type conversion on-the-fly. This conversion should be free of side-effects and, in particular, shall not throw exceptions, because a failed projection is not an error. A pattern simply does not match if a projection failed. Let us have a look at a simple example.
The lambda intproj is a string ⇒ int projection, but note that it does not return an integer.
It returns option<int>, because the projection is not guaranteed to always succeed.
An empty option indicates, that a value does not have a valid mapping to an integer.
A pattern does not match if a projection failed.
Note: Functors used as projection must take exactly one argument and must return a value.
The types for the pattern are deduced from the functor’s signature.
If the functor returns an option<T>, then T is deduced.
libcppa provides three actor implementations, each covering a particular use case.
The class local_actor is the base class for all implementations, except for (remote) proxy actors.
The class local_actor describes a local running actor.
It provides a common interface for actor operations like trapping exit messages or finishing execution.
The self pointer is an essential ingredient of our design.
It identifies the running actor similar to the implicit this pointer identifying an object within a member function.
Unlike this, though, self is not limited to a particular scope.
The self pointer is used implicitly, whenever an actor calls functions like send or receive, but can be accessed to use more advanced actor operations such as linking to another actor, e.g., by calling self->link_to(other).
The self pointer is convertible to actor_ptr and local_actor*, but it is neither copyable nor assignable.
Thus, auto s = self will cause a compiler error, while actor_ptr s = self works as expected.
A thread that accesses self is converted on-the-fly to an actor if needed.
Hence, “everything is an actor” in libcppa.
| Member functions | |
quit(uint32_t reason = normal) | Finishes execution of this actor |
| Observers | |
bool trap_exit() | Checks whether this actor traps exit messages |
bool chaining() | Checks whether this actor uses the “chained send” optimization (see Section 5.2) |
any_tuple last_dequeued() | Returns the last message that was dequeued from the actor’s mailbox |
| Note: Only set during callback invocation | |
actor_ptr last_sender() | Returns the sender of the last dequeued message |
| Note1: Only set during callback invocation | |
Note2: Used by the function reply (see Section 5.1) | |
| Modifiers | |
void trap_exit(bool enabled) | Enables or disables trapping of exit messages |
void chaining(bool enabled) | Enables or disables chained send |
We have already shown the differences of context-switching and event-based actors in Section 6. Context-switching and event-based actors are scheduled cooperatively in a thread pool. Developers can opt-out of this cooperative scheduling by using thread-mapped actors.
This is the implicit type of all threads that were converted to actors implicitly.
Furthermore, this type is used for actors created with spawn<detached>.
It is recommended to use detached actors whenever an actor could starve other actors, e.g., by calling time-expensive, blocking system calls.
Detached actors also could be used for actors that need to stay responsive, independent of the current work load.
However, threads do not scale well.
Hence, detached actors should be used only in small numbers for long-lived actors.
Context-switching actors have an own control flow and allow developers to spawn arbitrary functions as actors. The downside of context-switching actors is that each actor needs to allocate its own stack. This seriously impacts the performance for short-lived actors and is not applicable for large-scale actor systems. This implementations allows for an easy migration of previously threaded application, but a system should not contain more than a few hundred context-switching actors.
This is the recommended implementation for most use cases. Event-based actors have a small memory footprint and are thus very lightweight. The behavior-based API makes it harder to nest receives, but this implementation clearly scales best. See Section 6.2 for a few examples.
Messages can be sent by using either the function send, or send_tuple, or operator<<. The variadic template function send has the following signature.
The variadic template pack what... is converted to a dynamically typed tuple (see Section 2.1) and then enqueued to the mailbox of whom.
The following example shows two equal sends, one using send and the other using operator<<.
Using the function send is more compact, but does not have any other benefit.
However, note that you should not use send if you already have an instance of any_tuple, because it creates a new tuple containing the old one.
The function send_tuple is equal to operator<<.
Choosing one or the other is merely a matter of personal preferences.
During callback invokation, self->last_sender() is set.
This identifies the sender of the received message and is used implicitly by the functions reply and reply_tuple.
Note that reply(...) is not equal to send(self->last_sender(), ...).
The function send always uses asynchronous message passing, whereas reply will send a synchronous response message if the received message was a synchronous request
(see Section 7).
Sending a message to a cooperatively scheduled actor usually causes the receiving actor to be put into the scheduler’s job queue if it is currently blocked, i.e., is waiting for a new message. This job queue is accessed by worker threads. The chaining optimization does not cause the receiver to be put into the scheduler’s job queue if it is currently blocked. The receiver is stored as successor of the currently running actor instead. Hence, the active worker thread does not need to access the job queue, which significantly speeds up execution. However, this optimization can be inefficient if an actor first sends a message and then starts computation.
The example above illustrates an inefficient work flow.
The actor other is marked as successor of the foo actor but its execution is delayed until very_long_computation() is done.
In general, actors should follow the work flow receive ⇒compute ⇒ send results.
However, this optimization can be disabled by calling self->chaining(false) if an actor does not match this work flow.
Messages can be delayed, e.g., to implement time-based polling strategies, by using one of the functions delayed_send, delayed_send_tuple, delayed_reply, or delayed_reply_tuple.
The following example illustrates a polling strategy using delayed_send.
The function 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 B to actor C.
A B C
| | |
| --(sync_send)--> | |
| | --(forward_to)-> |
| X |---\
| | | compute
| | | result
| |<--/
| <-------------(reply)-------------- |
| X
|---\
| | handle
| | response
|<--/
|
X
The forwarding is completely transparent to actor C, since it will see actor A as sender of the message.
However, actor A will see actor C as sender of the response message instead of actor B and thus could recognize the forwarding by evaluating self->last_sender().
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, whereas the latter use an explicit receive function. The current behavior of an actor is its response to the next incoming message and includes (a) sending messages to other actors, (b) creation of more actors, and (c) setting a new behavior.
The function 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.
An actor calling receive is blocked until it successfully dequeued a message from its mailbox or an optional timeout occurs.
The code snippet above illustrates the use of receive.
Note that the partial function passed to receive is a temporary object at runtime.
Hence, using receive inside a loop would cause creation of a new partial function on each iteration.
libcppa provides three predefined receive loops to provide a more efficient but yet convenient way of defining receive loops.
| |
for (;;) {
receive (
// ...
);
} | receive_loop (
// ...
); |
std::vector<int> results;
for (size_t i = 0; i < 10; ++i) {
receive (
on<int>() >> [&](int value) {
results.push_back(value);
}
);
} | std::vector<int> results;
size_t i = 0;
receive_for(i, 10) (
on<int>() >> [&](int value) {
results.push_back(value);
}
); |
size_t received = 0;
do {
receive (
others() >> [&]() {
++received;
}
);
} while (received < 10); | size_t received = 0;
do_receive (
others() >> [&]() {
++received;
}
).until(gref(received) >= 10); |
The examples above illustrate the correct usage of the three loops receive_loop, receive_for and do_receive(...).until.
It is possible to nest receives and receive loops.
An event-based actor uses become to set its behavior.
The given behavior is then executed until it is replaced by another call to become or the actor finishes execution.
A subtype of event_based_actor must implement the pure virtual member function init.
An implementation of init shall set an initial behavior by using become.
Another way to implement event-based actors is provided by the class sb_actor (“State-Based Actor”).
This base class calls become(init_state) in its init member function.
Hence, a subclass must only provide a member of type behavior named init_state.
Note that sb_actor uses the Curiously Recurring Template Pattern. Thus, the derived class must be given as template parameter.
This technique allows sb_actor to access the init_state member of a derived class.
The following example illustrates a more advanced state-based actor that implements a stack with a fixed maximum number of elements. Note that this example uses non-static member initialization and thus might not compile with some compilers.
become/unbecomeNesting receives in an event-based actor is slightly more difficult compared to context-switching or thread-mapped actors, since become does not block.
An actor has to set a new behavior calling become with the keep_behavior policy to wait for the required message and then return to the previous behavior by using unbecome, as shown in the example below.
An event-based actor finishes execution with normal exit reason if the behavior stack is empty after calling unbecome.
The default policy of become is discard_behavior that causes an actor to override its current behavior.
The policy flag must be the first argument of become.
The previously introduced ways to define event-based actors always required a class definition.
A factory provides an ad-hoc way to define event-based actors using lambda expressions or other functors.
The factory factory::event_based takes a functor that is used as event_based_actor::init implementation.
Hence, the functor should call become to set an initial behavior.
Note that you have to call self->become, since become is not available via the this pointer.
Though event_based_actor::init has zero arguments, the functor can take any number of pointer arguments.
The factory then creates an actor with a member variable for each of those arguments and calls the functor with pointers to the actor’s member variables.
The member variables can be initialized with user-defined values passed to the spawn member function of the created factory, as shown in the following example.
During receive, an actor is blocked until it dequeues a message from its mailbox that matches the given pattern.
If no such message ever arrives, the actor is blocked 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 messages does not arrive within a certain time period. The following examples illustrates the usage of after to define a timeout.
Callbacks given as timeout handler must have zero arguments.
Any number of patterns can precede the timeout definition, but “after” must always be the final statement.
Using a zero-duration timeout causes receive to not block.
libcppa supports minutes, seconds, milliseconds and 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.
libcppa uses a future-based API for synchronous communication.
The functions sync_send and sync_send_tuple send synchronous request messages to the receiver and return a future to the response message.
Note that the returned future is actor-local, i.e., only the actor that has send the corresponding request message is able to receive the response identified by such a future.
A synchronous message is sent to the receiving actor’s mailbox like any other asynchronous message. The response message, on the other hand, is treated separately.
Note: the runtime system will automatically reply with an empty message if the receiving actor did not respond to a received synchronous response message by using the function reply.
The functions receive_response and handle_response can be used to receive response messages, as shown in the following example.
The function receive_response is similar to receive, i.e., it blocks the calling actor until either a response message was received or a timeout occured.
Similar to become, the function handle_response is part of the event-based API and is used as “one-shot handler” to respond to a given future.
The behavior passed to handle_response is executed once and the actor automatically returns to its previous behavior afterwards.
It is possible to “stack” multiple handle_response calls.
Each response handler is executed once and then automatically discarded.
In both cases, the behavior definition of the response handler requires a timeout.
message_future’s Member Functions to Receive a ResponseOften, an actor sends a synchronous message and then wants to wait for the response.
In this case, using either handle_response or receive_response is quite verbose.
Therefore, message_future provides the two member functions then and await.
Using then is equal to using handle_response, wheres await corresponds to receive_response, as illustrated by the following example.
libcppa 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.
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 trap exit messages to handle them manually.
A monitor observes the lifetime of an actor. Monitored actors send a down message to all monitors as part of their termination. Unlike exit messages, down messages are always treated like any other ordinary message.
Monitors are redundant. Hence, actors will receive one down message for each monitor.
All error codes are defined in the namespace cppa::exit_reason.
normal | Actor finished execution without error |
unhandled_exception | Actor was killed due to an unhandled exception |
unallowed_function_call | Indicates that an event-based actor tried to use blocking receive calls |
remote_link_unreachable | Indicates that a remote actor became unreachable, e.g., due to connection error |
user_defined | Minimum value for user-defined exit codes |
Actors can attach cleanup code to other actors.
This code is executed immediately if the actor has already exited.
Keep in mind that self refers to the currently running actor.
Thus, self refers to the terminating actor and not to the actor that attached a functor to it.
Note: It is possible to attach code to remote actors, but the cleanup code will run on the local machine.
Actors are created using the function spawn.
The arguments passed to spawn depend on the actor’s implementation.
The recommended way to implement both context-switching and thread-mapped actors is to use functors, e.g., a free function or lambda expression.
The arguments to the functor are passed to spawn as additional arguments.
The optional scheduling_hint template parameter of spawn decides whether an actor should run in its own thread or use context-switching.
The flag detached causes spawn to create a thread-mapped actor, whereas scheduled, the default flag, causes it to create a context-switching actor.
The function spawn provides a quite similar usage as std::thread, as shown in the examples below.
Though it is possible to subtype context_switching_actor to implement a class-based actor using context-switching, it is not recommended.
In general, context-switching and thread-mapped actors are intended to ease migration of existing applications or to implement managing actors on-the-fly using lambda expressions.
Class-based actors should be a subtype of event_based_actor, since this is the recommended actor implementation of libcppa.
Note: spawn(fun, arg0, ...) is not the same as spawn(std::bind(fun, arg0, ...))!
For example, a call to spawn(fun, self, ...) will pass a pointer to the calling actor to the newly created actor, as expected, whereas spawn(std::bind(fun, self, ...)) wraps the type of self into the function wrapper and evaluates self on function invocation.
Thus, the actor will end up having a pointer to itself rather than a pointer to the actor that created it.
Spawning class-based actors is straightforward and uses the function spawn as well.
The template parameter is the implementing class rather than a scheduling_hint, since class-based actors are always scheduled.
All arguments are forwarded to the constructor, as shown in the following example.
For spawning event-based actors without implementing an own class see Section 6.2.3. To spawn actors as members of a group see Section 11.3.
All actor operations as well as sending messages are network transparent. Remote actors are represented by actor proxies that forward all messages.
The function publish binds an actor to a given port.
It throws network_error if socket related errors occur or bind_failure if the specified port is already in use.
The function remote_actor connects to the actor at given host and port.
A network_error is thrown if the connection failed.
libcppa supports publish/subscribe-based group communication. Actors can join and leave groups and send messages to groups.
Groups created on-the-fly with group::anonymous() can be used to coordinate a set of workers.
Each call to group::anonymous() returns a new group instance.
The group module creates groups for in-process communication.
For example, a group for GUI related events could be identified by "local"group::get("local", "GUI events").
The group ID uniquely identifies a singleton group instance of the module "GUI events"."local"
The function spawn_in_group can be used to create actors as members of a group.
The function causes the newly created actors to call self->join(...) immediately and before spawn_in_group returns.
The usage of spawn_in_group is equal to spawn, except for an additional group argument.
The group handle is always the first argument, as shown in the examples below.
become and handle_response do not block, i.e., always return immediately.
Thus, you should always capture by value in event-based actors, because all references on the stack will cause undefined behavior if a lambda is executed.
send(self->last_sender(), ...) is not equal to reply(...).
The two functions receive_response and handle_response will only recognize messages send via either reply or reply_tuple.sync_send represents exactly one response message.
Therefore, it is not possible to receive more than one response message.
Calling reply more than once will result in lost messages and calling handle_response or receive_response more than once on a future will throw an exception.sync_send is bound to the calling actor.
It is not possible to transfer such a future to another actor.
Calling receive_response or handle_response for a future bound to another actor is undefined behavior.send(whom, ...) is syntactic sugar for whom << make_any_tuple(...).
Hence, a message sent via send(whom, self->last_dequeued()) will not yield the expected result, since it wraps self->last_dequeued() into another any_tuple instance.
The correct way of forwarding messages is self->forward_to(whom).std::shared_ptr.
Nevertheless, the recommended way of sharing informations is message passing.
Sending data to multiple actors does not result in copying the data several times.
Read Section 2 to learn more about libcppa’s copy-on-write optimization for tuples.
libcppa provides a fully network transparent communication between actors.
Thus, libcppa needs to serialize and deserialize messages.
Unfortunately, this is not possible using the RTTI system of C++.
libcppa uses its own RTTI based on the class uniform_type_info, since it is not possible to extend std::type_info.
Unlike std::type_info::name(), 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.
However, you should rarely if ever need to use object or uniform_type_info.
All user-defined types must be explicitly “announced” so that libcppa can (de)serialize them correctly, as shown in the example below.
Without the announce function call, the example program would terminate with an exception, because libcppa rejects all types without available runtime type information.
announce() takes the class as template parameter and pointers to all members (or getter/setter pairs) as arguments.
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 announce_example_5.cpp in the examples folder.
optionDefined in header ."cppa/option.hpp"
Represents an optional value.
| Member types | |
| Member type | Definition |
type | T |
| Member functions | |
option() | Constructs an empty option |
option(T value) | Initializes this with value |
option(const option&) | |
option(option&&) | Copy/move construction |
option& operator=(const option&) | |
option& operator=(option&&) | Copy/move assignment |
| Observers | |
bool valid() | |
explicit operator bool() | Returns true if this has a value |
bool empty() | |
bool operator!() | Returns true if this does not has a value |
const T& get() | |
const T& operator*() | Access stored value |
const T& get_or_else(const T& x) | Returns get() if valid, x otherwise |
| Modifiers | |
T& get() | |
T& operator*() | Access stored value |
This document was translated from LATEX by HEVEA.