Loading...
Searching...
No Matches
Blocking API

Blocking functions to receive messages.

Blocking functions to receive messages.

The blocking API of CAF is intended to be used for migrating previously threaded applications. When writing new code, you should consider the nonblocking API based on become and unbecome first.

# Sending Messages

The function send can be used to send a message to an actor. The first argument is the receiver of the message followed by any number of values:

~~ // spawn some actors actor_system_config cfg; actor_system system{cfg}; auto a1 = system.spawn(...); auto a2 = system.spawn(...); auto a3 = system.spawn(...);

// an actor executed in the current thread scoped_actor self{system};

// define an atom for message annotation using hello_atom = atom_constant<atom("hello")>; using compute_atom = atom_constant<atom("compute")>; using result_atom = atom_constant<atom("result")>;

// send a message to a1 self->send(a1, hello_atom_v, "hello a1!");

// send a message to a1, a2, and a3 auto msg = make_message(compute_atom_v, 1, 2, 3); self->send(a1, msg); self->send(a2, msg); self->send(a3, msg); ~~

# Receive messages

The function receive takes a behavior as argument. The behavior is a list of { callback } rules where the callback argument types define a pattern for matching messages.

~~ { [](hello_atom, const std::string& msg) { cout << "received hello message: " << msg << endl; }, [](compute_atom, int i0, int i1, int i2) { // send our result back to the sender of this messages return make_message(result_atom_v, i0 + i1 + i2); } } ~~

Blocking actors such as the scoped actor can call their receive member to handle incoming messages.

~~ self->receive( [](result_atom, int i) { cout << "result is: " << i << endl; } ); ~~

Please read the manual for further details about pattern matching.

# Atoms

Atoms are a nice way to add semantic informations to a message. Assuming an actor wants to provide a "math sevice" for integers. It could provide operations such as addition, subtraction, etc. This operations all have two operands. Thus, the actor does not know what operation the sender of a message wanted by receiving just two integers.

Example actor: ~~ using plus_atom = atom_constant<atom("plus")>; using minus_atom = atom_constant<atom("minus")>; behavior math_actor() { return { [](plus_atom, int a, int b) { return make_message(atom("result"), a + b); }, [](minus_atom, int a, int b) { return make_message(atom("result"), a - b); } }; } ~~

# Receive Loops

The previous examples used receive to create a behavior on-the-fly. This is inefficient in a loop since the argument passed to receive is created in each iteration again. It's possible to store the behavior in a variable and pass that variable to receive. This fixes the issue of re-creation each iteration but rips apart definition and usage.

There are three convenience functions implementing receive loops to declare behavior where it belongs without unnecessary copies: receive_while, receive_for and do_receive.

receive_while creates a functor evaluating a lambda expression. The loop continues until the given lambda returns false. A simple example:

~~ size_t received = 0; receive_while([&] { return received < 10; }) ( [&](int) { ++received; } ); // ... ~~

receive_for is a simple ranged-based loop:

~~ std::vector<int> results; size_t i = 0; receive_for(i, 10) ( [&](int value) { results.push_back(value); } ); ~~

do_receive returns a functor providing the function until that takes a lambda expression. The loop continues until the given lambda returns true. Example:

~~ size_t received = 0; do_receive ( [&](int) { ++received; } ).until([&] { return received >= 10; }); // ... ~~

# Sending Delayed Messages

The function delayed_send provides a simple way to delay a message. This is particularly useful for recurring events, e.g., periodical polling. Usage example:

~~ scoped_actor self{...};

self->delayed_send(self, std::chrono::seconds(1), poll_atom_v); bool running = true; self->receive_while([&](){ return running; }) ( // ... [&](poll_atom) { // ... poll something ... // and do it again after 1sec self->delayed_send(self, std::chrono::seconds(1), poll_atom_v); } ); ~~

See also the `dancing kirby example`.