Commit 30c319af authored by Dominik Charousset's avatar Dominik Charousset

Clean up manual sections and examples

parent f503a0f4
/******************************************************************************\ // This example program showcases how to manually manage socket I/O using a
* This example program showcases how to manually manage socket IO using * // broker. Server and client exchange integers in a "ping-pong protocol".
* a broker. Server and client exchange integers in a 'ping-pong protocol'. * //
* * // Minimal setup:
* Minimal setup: * // - ./build/bin/broker -s 4242
* - ./build/bin/broker -s 4242 * // - ./build/bin/broker -c localhost 4242
* - ./build/bin/broker -c localhost 4242 *
\
******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
#include "caf/config.hpp" #include "caf/config.hpp"
...@@ -39,12 +34,14 @@ using namespace caf::io; ...@@ -39,12 +34,14 @@ using namespace caf::io;
namespace { namespace {
// --(rst-attach-begin)--
// Utility function to print an exit message with custom name. // Utility function to print an exit message with custom name.
void print_on_exit(const actor& hdl, const std::string& name) { void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](const error& reason) { hdl->attach_functor([=](const error& reason) {
cout << name << " exited: " << to_string(reason) << endl; cout << name << " exited: " << to_string(reason) << endl;
}); });
} }
// --(rst-attach-end)--
enum class op : uint8_t { enum class op : uint8_t {
ping, ping,
......
// Showcases how to add custom POD message types. // Showcases how to add custom POD message types.
// Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications)
// 23-33 (TypeInspection)
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <string> #include <string>
...@@ -117,4 +114,4 @@ void caf_main(actor_system& sys) { ...@@ -117,4 +114,4 @@ void caf_main(actor_system& sys) {
self->send(t, foo_pair2{3, 4}); self->send(t, foo_pair2{3, 4});
} }
CAF_MAIN(id_block::custom_types_1) CAF_MAIN(caf::id_block::custom_types_1)
// showcases how to add custom message types to CAF // Showcases how to add custom message types to CAF if friend access for
// if friend access for serialization is available // serialization is available.
#include <utility> #include <utility>
#include <iostream> #include <iostream>
......
// Showcases custom message types that cannot provide // Showcases custom message types that cannot provide friend access to the
// friend access to the inspect() function. // inspect() function.
#include <iostream> #include <iostream>
#include <utility> #include <utility>
......
/******************************************************************************\ // This example is an implementation of the classical Dining Philosophers
* This example is an implementation of the classical Dining Philosophers * // exercise using only libcaf's event-based actor implementation.
* exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
......
/******************************************************************************\ // This example is a very basic, non-interactive math service implemented for
* This example is a very basic, non-interactive math service implemented * // both the blocking and the event-based API.
* for both the blocking and the event-based API. *
\******************************************************************************/
// Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
#include <iostream> #include <iostream>
...@@ -14,10 +10,13 @@ using namespace caf; ...@@ -14,10 +10,13 @@ using namespace caf;
namespace { namespace {
// --(rst-actor-begin)--
using calculator_actor using calculator_actor
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>, = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>; replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
// --(rst-actor-end)--
// --(rst-fwd-begin)--
// prototypes and forward declarations // prototypes and forward declarations
behavior calculator_fun(event_based_actor* self); behavior calculator_fun(event_based_actor* self);
void blocking_calculator_fun(blocking_actor* self); void blocking_calculator_fun(blocking_actor* self);
...@@ -25,7 +24,9 @@ calculator_actor::behavior_type typed_calculator_fun(); ...@@ -25,7 +24,9 @@ calculator_actor::behavior_type typed_calculator_fun();
class calculator; class calculator;
class blocking_calculator; class blocking_calculator;
class typed_calculator; class typed_calculator;
// --(rst-fwd-end)--
// --(rst-funs-begin)--
// function-based, dynamically typed, event-based API // function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) { behavior calculator_fun(event_based_actor*) {
return { return {
...@@ -55,7 +56,9 @@ calculator_actor::behavior_type typed_calculator_fun() { ...@@ -55,7 +56,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
[](sub_atom, int32_t a, int32_t b) { return a - b; }, [](sub_atom, int32_t a, int32_t b) { return a - b; },
}; };
} }
// --(rst-funs-end)--
// --(rst-classes-begin)--
// class-based, dynamically typed, event-based API // class-based, dynamically typed, event-based API
class calculator : public event_based_actor { class calculator : public event_based_actor {
public: public:
...@@ -91,6 +94,7 @@ public: ...@@ -91,6 +94,7 @@ public:
return typed_calculator_fun(); return typed_calculator_fun();
} }
}; };
// --(rst-classes-end)--
void tester(scoped_actor&) { void tester(scoped_actor&) {
// end of recursion // end of recursion
...@@ -121,12 +125,14 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y, ...@@ -121,12 +125,14 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
// --(rst-spawn-begin)--
auto a1 = system.spawn(blocking_calculator_fun); auto a1 = system.spawn(blocking_calculator_fun);
auto a2 = system.spawn(calculator_fun); auto a2 = system.spawn(calculator_fun);
auto a3 = system.spawn(typed_calculator_fun); auto a3 = system.spawn(typed_calculator_fun);
auto a4 = system.spawn<blocking_calculator>(); auto a4 = system.spawn<blocking_calculator>();
auto a5 = system.spawn<calculator>(); auto a5 = system.spawn<calculator>();
auto a6 = system.spawn<typed_calculator>(); auto a6 = system.spawn<typed_calculator>();
// --(rst-spawn-end)--
scoped_actor self{system}; scoped_actor self{system};
tester(self, a1, 1, 2, a2, 3, 4, a3, 5, 6, a4, 7, 8, a5, 9, 10, a6, 11, 12); tester(self, a1, 1, 2, a2, 3, 4, a3, 5, 6, a4, 7, 8, a5, 9, 10, a6, 11, 12);
self->send_exit(a1, exit_reason::user_shutdown); self->send_exit(a1, exit_reason::user_shutdown);
......
/******************************************************************************\ // This example is a very basic, non-interactive math service implemented for
* This example is a very basic, non-interactive math service implemented * // both the blocking and the event-based API.
* for both the blocking and the event-based API. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-44, and 49-50 (Actor.tex)
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
...@@ -16,6 +10,7 @@ using std::cout; ...@@ -16,6 +10,7 @@ using std::cout;
using std::endl; using std::endl;
using namespace caf; using namespace caf;
// --(rst-cell-begin)--
using cell = typed_actor<reacts_to<put_atom, int32_t>, using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>; replies_to<get_atom>::with<int32_t>>;
...@@ -24,14 +19,19 @@ struct cell_state { ...@@ -24,14 +19,19 @@ struct cell_state {
}; };
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) { cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return {[=](put_atom, int32_t val) { self->state.value = val; }, return {
[=](get_atom) { return self->state.value; }}; [=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
} }
behavior unchecked_cell(stateful_actor<cell_state>* self) { behavior unchecked_cell(stateful_actor<cell_state>* self) {
return {[=](put_atom, int32_t val) { self->state.value = val; }, return {
[=](get_atom) { return self->state.value; }}; [=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
} }
// --(rst-cell-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
// create one cell for each implementation // create one cell for each implementation
......
/******************************************************************************\ // This example illustrates how to do time-triggered loops in CAF.
* This example illustrates how to do time-triggered loops in libcaf. *
\******************************************************************************/
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
...@@ -8,29 +6,26 @@ ...@@ -8,29 +6,26 @@
#include "caf/all.hpp" #include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 58-75 (MessagePassing.tex)
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::pair; using std::pair;
using namespace caf; using namespace caf;
// ASCII art figures // ASCII art figures.
constexpr const char* figures[] = { constexpr const char* figures[] = {
"<(^.^<)", "<(^.^<)",
"<(^.^)>", "<(^.^)>",
"(>^.^)>", "(>^.^)>",
}; };
// Bundes an index to an ASCII art figure plus its position on screen.
struct animation_step { struct animation_step {
size_t figure_idx; size_t figure_idx;
size_t offset; size_t offset;
}; };
// array of {figure, offset} pairs // Array of {figure, offset} pairs.
constexpr animation_step animation_steps[] = { constexpr animation_step animation_steps[] = {
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6}, {1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9}, {2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
...@@ -38,41 +33,48 @@ constexpr animation_step animation_steps[] = { ...@@ -38,41 +33,48 @@ constexpr animation_step animation_steps[] = {
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7}, {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7},
}; };
// Width of the printed area.
constexpr size_t animation_width = 20; constexpr size_t animation_width = 20;
// "draws" an animation step by printing "{offset_whitespaces}{figure}{padding}" // Draws an animation step by printing "{offset_whitespaces}{figure}{padding}".
void draw_kirby(const animation_step& animation) { void draw_kirby(const animation_step& animation) {
cout.width(animation_width); cout.width(animation_width);
// override last figure // Override last figure.
cout << '\r'; cout << '\r';
// print offset // Print left padding (offset).
std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' '); std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' ');
// print figure // Print figure.
cout << figures[animation.figure_idx]; cout << figures[animation.figure_idx];
// print padding // Print right padding.
cout.fill(' '); cout.fill(' ');
// make sure figure is printed // Make sure figure is visible.
cout.flush(); cout.flush();
} }
// uses a message-based loop to iterate over all animation steps // --(rst-dancing-kirby-begin)--
void dancing_kirby(event_based_actor* self) { // Uses a message-based loop to iterate over all animation steps.
// let's get it started behavior dancing_kirby(event_based_actor* self) {
self->send(self, update_atom_v, size_t{0}); // Let's get started.
self->become([=](update_atom, size_t step) { auto i = std::begin(animation_steps);
if (step == sizeof(animation_step)) { auto e = std::end(animation_steps);
// we've printed all animation steps (done) self->send(self, update_atom_v);
cout << endl; return {
self->quit(); [=](update_atom) mutable {
return; // We're done when reaching the past-the-end position.
} if (i == e) {
// print given step cout << endl;
draw_kirby(animation_steps[step]); self->quit();
// animate next step in 150ms return;
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v, }
step + 1); // Print current animation step.
}); draw_kirby(*i);
// Animate next step in 150ms.
++i;
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v);
},
};
} }
// --(rst-dancing-kirby-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
system.spawn(dancing_kirby); system.spawn(dancing_kirby);
......
#include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 15-36 (MessagePassing.tex)
using namespace caf; using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) // --(rst-delegate-begin)--
using calc = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>; using calc = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
void actor_a(event_based_actor* self, const calc& worker) { void actor_a(event_based_actor* self, const calc& worker) {
...@@ -32,6 +28,7 @@ calc::behavior_type actor_c() { ...@@ -32,6 +28,7 @@ calc::behavior_type actor_c() {
[](add_atom, int32_t x, int32_t y) { return x + y; }, [](add_atom, int32_t x, int32_t y) { return x + y; },
}; };
} }
// --(rst-delegate-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c))); system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c)));
......
/******************************************************************************\ // A basic, interactive divider.
* A very basic, interactive divider. *
\******************************************************************************/
// Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 17-47 (Error)
#include <iostream> #include <iostream>
...@@ -14,10 +9,21 @@ using std::endl; ...@@ -14,10 +9,21 @@ using std::endl;
using std::flush; using std::flush;
using namespace caf; using namespace caf;
enum class math_error : uint8_t;
CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_ADD_TYPE_ID(divider, (math_error))
CAF_END_TYPE_ID_BLOCK(divider)
// --(rst-divider-begin)--
enum class math_error : uint8_t { enum class math_error : uint8_t {
division_by_zero = 1, division_by_zero = 1,
}; };
CAF_ERROR_CODE_ENUM(math_error)
std::string to_string(math_error x) { std::string to_string(math_error x) {
switch (x) { switch (x) {
case math_error::division_by_zero: case math_error::division_by_zero:
...@@ -27,14 +33,6 @@ std::string to_string(math_error x) { ...@@ -27,14 +33,6 @@ std::string to_string(math_error x) {
} }
} }
CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_ADD_TYPE_ID(divider, (math_error))
CAF_END_TYPE_ID_BLOCK(divider)
CAF_ERROR_CODE_ENUM(math_error)
using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>; using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>;
divider::behavior_type divider_impl() { divider::behavior_type divider_impl() {
...@@ -46,6 +44,7 @@ divider::behavior_type divider_impl() { ...@@ -46,6 +44,7 @@ divider::behavior_type divider_impl() {
}, },
}; };
} }
// --(rst-divider-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
double x; double x;
...@@ -54,6 +53,7 @@ void caf_main(actor_system& system) { ...@@ -54,6 +53,7 @@ void caf_main(actor_system& system) {
std::cin >> x; std::cin >> x;
cout << "y: " << flush; cout << "y: " << flush;
std::cin >> y; std::cin >> y;
// --(rst-request-begin)--
auto div = system.spawn(divider_impl); auto div = system.spawn(divider_impl);
scoped_actor self{system}; scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom_v, x, y) self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
...@@ -63,6 +63,7 @@ void caf_main(actor_system& system) { ...@@ -63,6 +63,7 @@ void caf_main(actor_system& system) {
aout(self) << "*** cannot compute " << x << " / " << y << " => " aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< to_string(err) << endl; << to_string(err) << endl;
}); });
// --(rst-request-end)--
} }
CAF_MAIN(id_block::divider) CAF_MAIN(id_block::divider)
#include "caf/all.hpp"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp"
enum class ec : uint8_t { enum class ec : uint8_t {
push_to_full = 1, push_to_full = 1,
pop_from_empty, pop_from_empty,
......
/******************************************************************************\ // Illustrates how to use response promises.
* Illustrates response promises. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-43 (MessagePassing.tex)
#include <iostream> #include <iostream>
...@@ -15,17 +9,16 @@ using std::endl; ...@@ -15,17 +9,16 @@ using std::endl;
using namespace caf; using namespace caf;
// --(rst-promises-begin)--
using adder using adder
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>; = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
// function-based, statically typed, event-based API
adder::behavior_type worker() { adder::behavior_type worker() {
return { return {
[](add_atom, int32_t a, int32_t b) { return a + b; }, [](add_atom, int32_t a, int32_t b) { return a + b; },
}; };
} }
// function-based, statically typed, event-based API
adder::behavior_type calculator_master(adder::pointer self) { adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker); auto w = self->spawn(worker);
return { return {
...@@ -38,6 +31,7 @@ adder::behavior_type calculator_master(adder::pointer self) { ...@@ -38,6 +31,7 @@ adder::behavior_type calculator_master(adder::pointer self) {
}, },
}; };
} }
// --(rst-promises-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master)); auto f = make_function_view(system.spawn(calculator_master));
......
/******************************************************************************\ // Illustrates semantics of request().{then|await|receive}.
* Illustrates semantics of request().{then|await|receive}. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
...@@ -16,8 +10,10 @@ ...@@ -16,8 +10,10 @@
using std::endl; using std::endl;
using std::vector; using std::vector;
using std::chrono::seconds; using std::chrono::seconds;
using namespace caf; using namespace caf;
// --(rst-cell-begin)--
using cell = typed_actor<reacts_to<put_atom, int32_t>, using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>; replies_to<get_atom>::with<int32_t>>;
...@@ -59,11 +55,14 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) { ...@@ -59,11 +55,14 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl; aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
}); });
} }
// --(rst-cell-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
vector<cell> cells; vector<cell> cells;
// --(rst-spawn-begin)--
for (auto i = 0; i < 5; ++i) for (auto i = 0; i < 5; ++i)
cells.emplace_back(system.spawn(cell_impl, i * i)); cells.emplace_back(system.spawn(cell_impl, i * i));
// --(rst-spawn-end)--
scoped_actor self{system}; scoped_actor self{system};
aout(self) << "waiting_testee" << endl; aout(self) << "waiting_testee" << endl;
auto x1 = self->spawn(waiting_testee, cells); auto x1 = self->spawn(waiting_testee, cells);
......
/******************************************************************************\ // This example is a very basic, non-interactive math service implemented using
* This example is a very basic, non-interactive math service implemented * // typed actors.
* using typed actors. *
\******************************************************************************/
#include "caf/all.hpp" #include "caf/all.hpp"
#include <cassert> #include <cassert>
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
// Run client at the same host: // Run client at the same host:
// - ./build/bin/distributed_math_actor -c -p 4242 // - ./build/bin/distributed_math_actor -c -p 4242
// Manual refs: 206-218 (ConfiguringActorSystems)
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <functional> #include <functional>
...@@ -199,6 +197,7 @@ optional<int> toint(const string& str) { ...@@ -199,6 +197,7 @@ optional<int> toint(const string& str) {
return none; return none;
} }
// --(rst-config-begin)--
class config : public actor_system_config { class config : public actor_system_config {
public: public:
uint16_t port = 0; uint16_t port = 0;
...@@ -212,6 +211,7 @@ public: ...@@ -212,6 +211,7 @@ public:
.add(server_mode, "server-mode,s", "enable server mode"); .add(server_mode, "server-mode,s", "enable server mode");
} }
}; };
// --(rst-config-end)--
void client_repl(actor_system& system, const config& cfg) { void client_repl(actor_system& system, const config& cfg) {
// keeps track of requests and tries to reconnect on server failures // keeps track of requests and tries to reconnect on server failures
......
/******************************************************************************\ // This example program represents a minimal terminal chat program based on
* This example program represents a minimal terminal chat program * // group communication.
* based on group communication. * //
* * // Setup for a minimal chat between "alice" and "bob":
* Setup for a minimal chat between "alice" and "bob": * // - ./build/bin/group_chat -s -p 4242
* - ./build/bin/group_chat -s -p 4242 * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\******************************************************************************/
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
......
/******************************************************************************\ // This example program represents a minimal IRC-like group communication
* This example program represents a minimal IRC-like group * // server.
* communication server. * //
* * // Setup for a minimal chat between "alice" and "bob":
* Setup for a minimal chat between "alice" and "bob": * // - ./build/bin/group_server -p 4242
* - ./build/bin/group_server -p 4242 * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\ ******************************************************************************/
#include <string> #include <string>
#include <cstdlib> #include <cstdlib>
......
// This program illustrates how to spawn a simple calculator // This program illustrates how to spawn a simple calculator across the network.
// across the network.
// //
// Run server at port 4242: // Run server at port 4242:
// - remote_spawn -s -p 4242 // - remote_spawn -s -p 4242
......
/****************************************************************************** // Basic, non-interactive streaming example for processing integers.
* Basic, non-interactive streaming example for processing integers. *
******************************************************************************/
#include <iostream> #include <iostream>
#include <vector> #include <vector>
......
// Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong #define CAF_SUITE ping_pong
#include "caf/test/dsl.hpp" #include "caf/test/dsl.hpp"
...@@ -9,6 +7,7 @@ ...@@ -9,6 +7,7 @@
using namespace caf; using namespace caf;
// --(rst-ping-pong-begin)--
namespace { namespace {
behavior ping(event_based_actor* self, actor pong_actor, int n) { behavior ping(event_based_actor* self, actor pong_actor, int n) {
...@@ -58,3 +57,4 @@ CAF_TEST(three pings) { ...@@ -58,3 +57,4 @@ CAF_TEST(three pings) {
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
// --(rst-ping-pong-end)--
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
// This file is referenced in the manual, do not modify without updating refs!
// ConfiguringActorApplications: 50-54
#pragma once #pragma once
#include <type_traits> #include <type_traits>
...@@ -45,9 +42,10 @@ struct is_allowed_unsafe_message_type<const T&> ...@@ -45,9 +42,10 @@ struct is_allowed_unsafe_message_type<const T&>
} // namespace caf } // namespace caf
// --(rst-macro-begin)--
#define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \ #define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \
namespace caf { \ namespace caf { \
template <> \ template <> \
struct allowed_unsafe_message_type<type_name> : std::true_type {}; \ struct allowed_unsafe_message_type<type_name> : std::true_type {}; \
} }
// --(rst-macro-end)--
...@@ -16,10 +16,6 @@ ...@@ -16,10 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 29-49 (Error.tex)
#pragma once #pragma once
#include <string> #include <string>
...@@ -28,6 +24,7 @@ ...@@ -28,6 +24,7 @@
namespace caf { namespace caf {
// --(rst-exit-reason-begin)--
/// This error category represents fail conditions for actors. /// This error category represents fail conditions for actors.
enum class exit_reason : uint8_t { enum class exit_reason : uint8_t {
/// Indicates that an actor finished execution without error. /// Indicates that an actor finished execution without error.
...@@ -49,6 +46,7 @@ enum class exit_reason : uint8_t { ...@@ -49,6 +46,7 @@ enum class exit_reason : uint8_t {
/// Indicates that an actor was killed because it became unreachable. /// Indicates that an actor was killed because it became unreachable.
unreachable unreachable
}; };
// --(rst-exit-reason-end)--
/// Returns a string representation of given exit reason. /// Returns a string representation of given exit reason.
std::string to_string(exit_reason); std::string to_string(exit_reason);
......
...@@ -16,10 +16,6 @@ ...@@ -16,10 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 32-117 (Error.tex)
#pragma once #pragma once
#include "caf/error.hpp" #include "caf/error.hpp"
...@@ -27,6 +23,7 @@ ...@@ -27,6 +23,7 @@
namespace caf { namespace caf {
// --(rst-sec-begin)--
/// SEC stands for "System Error Code". This enum contains error codes for /// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules. /// ::actor_system and its modules.
enum class sec : uint8_t { enum class sec : uint8_t {
...@@ -140,6 +137,7 @@ enum class sec : uint8_t { ...@@ -140,6 +137,7 @@ enum class sec : uint8_t {
/// Disconnected from a BASP node after reaching the connection timeout. /// Disconnected from a BASP node after reaching the connection timeout.
connection_timeout, connection_timeout,
}; };
// --(rst-sec-end)--
/// @relates sec /// @relates sec
std::string to_string(sec); std::string to_string(sec);
......
...@@ -3,19 +3,22 @@ ...@@ -3,19 +3,22 @@
Actors Actors
====== ======
Actors in CAF are a lightweight abstraction for units of computations. They Actors in CAF represent a lightweight abstraction for units of computations.
are active objects in the sense that they own their state and do not allow They are active objects in the sense that they own their state and do not allow
others to access it. The only way to modify the state of an actor is sending others to access it. The only way to modify the state of an actor is sending
messages to it. messages to it.
CAF provides several actor implementations, each covering a particular use CAF provides several actor implementations, each covering a particular use
case. The available implementations differ in three characteristics: (1) case. The available implementations differ in three characteristics:
dynamically or statically typed, (2) class-based or function-based, and (3)
using asynchronous event handlers or blocking receives. These three #. dynamically or statically typed
characteristics can be combined freely, with one exception: statically typed #. class-based or function-based
actors are always event-based. For example, an actor can have dynamically typed #. using asynchronous event handlers or blocking receives
messaging, implement a class, and use blocking receives. The common base class
for all user-defined actors is called ``local_actor``. These three characteristics can be combined freely, with one exception:
statically typed actors are always event-based. For example, an actor can have
dynamically typed messaging, implement a class, and use blocking receives. The
common base class for all user-defined actors is called ``local_actor``.
Dynamically typed actors are more familiar to developers coming from Erlang or Dynamically typed actors are more familiar to developers coming from Erlang or
Akka. They (usually) enable faster prototyping but require extensive unit Akka. They (usually) enable faster prototyping but require extensive unit
...@@ -28,7 +31,7 @@ visible across multiple translation units. ...@@ -28,7 +31,7 @@ visible across multiple translation units.
Actors that utilize the blocking receive API always require an exclusive thread Actors that utilize the blocking receive API always require an exclusive thread
of execution. Event-based actors, on the other hand, are usually scheduled of execution. Event-based actors, on the other hand, are usually scheduled
cooperatively and are very lightweight with a memory footprint of only few cooperatively and are very lightweight with a memory footprint of only few
hundred bytes. Developers can exclude---detach---event-based actors that hundred bytes. Developers can exclude (detach) event-based actors that
potentially starve others from the cooperative scheduling while spawning it. A potentially starve others from the cooperative scheduling while spawning it. A
detached actor lives in its own thread of execution. detached actor lives in its own thread of execution.
...@@ -38,10 +41,10 @@ Environment / Actor Systems ...@@ -38,10 +41,10 @@ Environment / Actor Systems
--------------------------- ---------------------------
All actors live in an ``actor_system`` representing an actor environment All actors live in an ``actor_system`` representing an actor environment
including :ref:`scheduler`, :ref:`registry`, and optional components such as a including :ref:`scheduler`, :ref:`registry`, and optional components (modules)
:ref:`middleman`. A single process can have multiple ``actor_system`` instances, such as a :ref:`middleman`. A single process can have multiple ``actor_system``
but this is usually not recommended (a use case for multiple systems is to instances, but this is usually not recommended (a use case for multiple systems
strictly separate two or more sets of actors by running them in different is to strictly separate two or more sets of actors by running them in different
schedulers). For configuration and fine-tuning options of actor systems see schedulers). For configuration and fine-tuning options of actor systems see
:ref:`system-config`. A distributed CAF application consists of two or more :ref:`system-config`. A distributed CAF application consists of two or more
connected actor systems. We also refer to interconnected ``actor_system`` connected actor systems. We also refer to interconnected ``actor_system``
...@@ -71,19 +74,15 @@ user-defined actors are ``event_based_actor`` or ...@@ -71,19 +74,15 @@ user-defined actors are ``event_based_actor`` or
inherited from ``monitorable_actor`` and ``abstract_actor``. inherited from ``monitorable_actor`` and ``abstract_actor``.
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| **Types** | | | **Types** |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``mailbox_type`` | A concurrent, many-writers-single-reader queue type. | | ``mailbox_type`` | A concurrent, many-writers-single-reader queue type. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| | | | **Constructors** |
+-------------------------------------+--------------------------------------------------------+
| **Constructors** | |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``(actor_config&)`` | Constructs the actor using a config. | | ``(actor_config&)`` | Constructs the actor using a config. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| | | | **Observers** |
+-------------------------------------+--------------------------------------------------------+
| **Observers** | |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``actor_addr address()`` | Returns the address of this actor. | | ``actor_addr address()`` | Returns the address of this actor. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
...@@ -93,17 +92,15 @@ inherited from ``monitorable_actor`` and ``abstract_actor``. ...@@ -93,17 +92,15 @@ inherited from ``monitorable_actor`` and ``abstract_actor``.
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``execution_unit* context()`` | Returns underlying thread or current scheduler worker. | | ``execution_unit* context()`` | Returns underlying thread or current scheduler worker. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| | | | **Customization Points** |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| **Customization Points** | | | ``on_exit()`` | Performs cleanup steps. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``on_exit()`` | Can be overridden to perform cleanup code. | | ``initialize()`` | Installs an initial behavior. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``const char* name()`` | Returns a debug name for this actor type. | | ``const char* name()`` | Returns a debug name for this actor type. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| | | | **Actor Management** |
+-------------------------------------+--------------------------------------------------------+
| **Actor Management** | |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``link_to(other)`` | Links to ``other`` (see :ref:`link`). | | ``link_to(other)`` | Links to ``other`` (see :ref:`link`). |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
...@@ -117,16 +114,13 @@ inherited from ``monitorable_actor`` and ``abstract_actor``. ...@@ -117,16 +114,13 @@ inherited from ``monitorable_actor`` and ``abstract_actor``.
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``spawn<T>(xs...)`` | Spawns a new actor of type ``T``. | | ``spawn<T>(xs...)`` | Spawns a new actor of type ``T``. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| | | | **Message Processing** |
+-------------------------------------+--------------------------------------------------------+
| **Message Processing** | |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``T make_response_promise<Ts...>()``| Allows an actor to delay its response message. | | ``T make_response_promise<Ts...>()``| Allows an actor to delay its response message. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
| ``T response(xs...)`` | Convenience function for creating fulfilled promises. | | ``T response(xs...)`` | Convenience function for creating fulfilled promises. |
+-------------------------------------+--------------------------------------------------------+ +-------------------------------------+--------------------------------------------------------+
Class ``scheduled_actor`` Class ``scheduled_actor``
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -135,7 +129,7 @@ statically and dynamically typed event-based actors as well as brokers ...@@ -135,7 +129,7 @@ statically and dynamically typed event-based actors as well as brokers
:ref:`broker`. :ref:`broker`.
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| **Types** | | | **Types** |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| ``pointer`` | ``scheduled_actor*`` | | ``pointer`` | ``scheduled_actor*`` |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
...@@ -149,23 +143,17 @@ statically and dynamically typed event-based actors as well as brokers ...@@ -149,23 +143,17 @@ statically and dynamically typed event-based actors as well as brokers
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| ``exit_handler`` | ``function<void (pointer, exit_msg&)>`` | | ``exit_handler`` | ``function<void (pointer, exit_msg&)>`` |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| | | | **Constructors** |
+-------------------------------+--------------------------------------------------------------------------+
| **Constructors** | |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| ``(actor_config&)`` | Constructs the actor using a config. | | ``(actor_config&)`` | Constructs the actor using a config. |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| | | | **Termination** |
+-------------------------------+--------------------------------------------------------------------------+
| **Termination** | |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| ``quit()`` | Stops this actor with normal exit reason. | | ``quit()`` | Stops this actor with normal exit reason. |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| ``quit(error x)`` | Stops this actor with error ``x``. | | ``quit(error x)`` | Stops this actor with error ``x``. |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| | | | **Special-purpose Handlers** |
+-------------------------------+--------------------------------------------------------------------------+
| **Special-purpose Handlers** | |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
| ``set_exception_handler(F f)``| Installs ``f`` for converting exceptions to errors (see :ref:`error`). | | ``set_exception_handler(F f)``| Installs ``f`` for converting exceptions to errors (see :ref:`error`). |
+-------------------------------+--------------------------------------------------------------------------+ +-------------------------------+--------------------------------------------------------------------------+
...@@ -191,45 +179,37 @@ actor is considered *done* only after it returned from ``act`` (or ...@@ -191,45 +179,37 @@ actor is considered *done* only after it returned from ``act`` (or
from the implementation in function-based actors). A ``scoped_actor`` from the implementation in function-based actors). A ``scoped_actor``
sends its exit messages as part of its destruction. sends its exit messages as part of its destruction.
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| **Constructors** | | | **Constructors** |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``(actor_config&)`` | Constructs the actor using a config. | | ``(actor_config&)`` | Constructs the actor using a config. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| | | | **Customization Points** |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| **Customization Points** | | | ``void act()`` | Implements the behavior of the actor. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``void act()`` | Implements the behavior of the actor. | | **Termination** |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| | | | ``const error& fail_state()`` | Returns the current exit reason. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| **Termination** | | | ``fail_state(error x)`` | Sets the current exit reason. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``const error& fail_state()`` | Returns the current exit reason. | | **Actor Management** |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``fail_state(error x)`` | Sets the current exit reason. | | ``wait_for(Ts... xs)`` | Blocks until all actors ``xs...`` are done. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| | | | ``await_all_other_actors_done()`` | Blocks until all other actors are done. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| **Actor Management** | | | **Message Handling** | |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``wait_for(Ts... xs)`` | Blocks until all actors ``xs...`` are done. | | ``receive(Ts... xs)`` | Receives a message using the callbacks ``xs...``. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``await_all_other_actors_done()``| Blocks until all other actors are done. | | ``receive_for(T& begin, T end)`` | See :ref:`receive-loop`. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| | | | ``receive_while(F stmt)`` | See :ref:`receive-loop`. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| **Message Handling** | | | ``do_receive(Ts... xs)`` | See :ref:`receive-loop`. |
+----------------------------------+---------------------------------------------------+ +-----------------------------------+---------------------------------------------------+
| ``receive(Ts... xs)`` | Receives a message using the callbacks ``xs...``. |
+----------------------------------+---------------------------------------------------+
| ``receive_for(T& begin, T end)`` | See receive-loop_. |
+----------------------------------+---------------------------------------------------+
| ``receive_while(F stmt)`` | See receive-loop_. |
+----------------------------------+---------------------------------------------------+
| ``do_receive(Ts... xs)`` | See receive-loop_. |
+----------------------------------+---------------------------------------------------+
.. _interface: .. _interface:
...@@ -250,7 +230,8 @@ messaging interface for a simple calculator. ...@@ -250,7 +230,8 @@ messaging interface for a simple calculator.
.. literalinclude:: /examples/message_passing/calculator.cpp .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++ :language: C++
:lines: 17-18 :start-after: --(rst-actor-begin)--
:end-before: --(rst-actor-end)--
It is not required to create a type alias such as ``calculator_actor``, It is not required to create a type alias such as ``calculator_actor``,
but it makes dealing with statically typed actors much easier. Also, a central but it makes dealing with statically typed actors much easier. Also, a central
...@@ -300,18 +281,20 @@ parameter. For example, the following functions and classes represent actors. ...@@ -300,18 +281,20 @@ parameter. For example, the following functions and classes represent actors.
.. literalinclude:: /examples/message_passing/calculator.cpp .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++ :language: C++
:lines: 21-26 :start-after: --(rst-fwd-begin)--
:end-before: --(rst-fwd-end)--
Spawning an actor for each implementation is illustrated below. Spawning an actor for each implementation is illustrated below.
.. literalinclude:: /examples/message_passing/calculator.cpp .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++ :language: C++
:lines: 123-128 :start-after: --(rst-spawn-begin)--
:end-before: --(rst-spawn-end)--
Additional arguments to ``spawn`` are passed to the constructor of a Additional arguments to ``spawn`` are passed to the constructor of a class or
class or used as additional function arguments, respectively. In the example used as additional function arguments, respectively. In the example above, none
above, none of the three functions takes any argument other than the implicit of the three functions takes any argument other than the implicit (optional)
but optional ``self`` pointer. ``self`` pointer.
.. _function-based: .. _function-based:
...@@ -323,7 +306,7 @@ argument *can* be used to capture a pointer to the actor itself. The type ...@@ -323,7 +306,7 @@ argument *can* be used to capture a pointer to the actor itself. The type
of this pointer is usually ``event_based_actor*`` or of this pointer is usually ``event_based_actor*`` or
``blocking_actor*``. The proper pointer type for any ``blocking_actor*``. The proper pointer type for any
``typed_actor`` handle ``T`` can be obtained via ``typed_actor`` handle ``T`` can be obtained via
``T::pointer`` interface_. ``T::pointer`` (see :ref:`interface`).
Blocking actors simply implement their behavior in the function body. The actor Blocking actors simply implement their behavior in the function body. The actor
is done once it returns from that function. is done once it returns from that function.
...@@ -344,7 +327,8 @@ dynamically typed). ...@@ -344,7 +327,8 @@ dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++ :language: C++
:lines: 28-56 :start-after: --(rst-funs-begin)--
:end-before: --(rst-funs-end)--
.. _class-based: .. _class-based:
...@@ -363,15 +347,16 @@ Implementing an actor using a class requires the following: ...@@ -363,15 +347,16 @@ Implementing an actor using a class requires the following:
Implementing actors with classes works for all kinds of actors and allows Implementing actors with classes works for all kinds of actors and allows
simple management of state via member variables. However, composing states via simple management of state via member variables. However, composing states via
inheritance can get quite tedious. For dynamically typed actors, composing inheritance can get quite tedious. For dynamically typed actors, composing
states is particularly hard, because the compiler cannot provide much help. For states is particularly hard, because the compiler cannot provide much help.
statically typed actors, CAF also provides an API for composable
behaviors composable-behavior_ that works well with inheritance. The The following three classes implement the prototypes shown in :ref:`spawn` and
following three examples implement the forward declarations shown in again feature one blocking actor and two event-based actors (statically and
spawn_. dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp .. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++ :language: C++
:lines: 58-92 :start-after: --(rst-classes-begin)--
:end-before: --(rst-classes-end)--
.. _stateful-actor: .. _stateful-actor:
...@@ -390,77 +375,11 @@ typing as well as with dynamic typing. ...@@ -390,77 +375,11 @@ typing as well as with dynamic typing.
.. literalinclude:: /examples/message_passing/cell.cpp .. literalinclude:: /examples/message_passing/cell.cpp
:language: C++ :language: C++
:lines: 18-44 :start-after: --(rst-cell-begin)--
:end-before: --(rst-cell-end)--
Stateful actors are spawned in the same way as any other function-based actor Stateful actors are spawned in the same way as any other function-based actor
function-based_. (see :ref:`function-based`).
.. literalinclude:: /examples/message_passing/cell.cpp
:language: C++
:lines: 49-50
.. _composable-behavior:
Actors from Composable Behaviors :sup:`experimental`
-----------------------------------------------------
When building larger systems, it is often useful to implement the behavior of
an actor in terms of other, existing behaviors. The composable behaviors in
CAF allow developers to generate a behavior class from a messaging
interface interface_.
The base type for composable behaviors is ``composable_behavior<T>``,
where ``T`` is a ``typed_actor<...>``. CAF maps each
``replies_to<A,B,C>::with<D,E,F>`` in ``T`` to a pure virtual
member function with signature:
.. code-block:: C++
result<D, E, F> operator()(param<A>, param<B>, param<C>);.
Note that ``operator()`` will take integral types as well as atom constants
simply by value. A ``result<T>`` accepts either a value of type ``T``, a
``skip_t`` (see :ref:`default-handler`), an ``error`` (see :ref:`error`), a
``delegated<T>`` (see :ref:`delegate`), or a ``response_promise<T>`` (see
:ref:`promise`). A ``result<void>`` is constructed by returning ``unit``.
A behavior that combines the behaviors ``X``, ``Y``, and
``Z`` must inherit from ``composed_behavior<X,Y,Z>`` instead of
inheriting from the three classes directly. The class
``composed_behavior`` ensures that the behaviors are concatenated
correctly. In case one message handler is defined in multiple base types, the
*first* type in declaration order wins. For example, if ``X`` and
``Y`` both implement the interface
``replies_to<int,int>::with<int>``, only the handler implemented in
``X`` is active.
Any composable (or composed) behavior with no pure virtual member functions can
be spawned directly through an actor system by calling
``system.spawn<...>()``, as shown below.
.. literalinclude:: /examples/composition/calculator_behavior.cpp
:language: C++
:lines: 20-45
The second example illustrates how to use non-primitive values that are wrapped
in a ``param<T>`` when working with composable behaviors. The purpose
of ``param<T>`` is to provide a single interface for both constant and
non-constant access. Constant access is modeled with the implicit conversion
operator to a const reference, the member function ``get()``, and
``operator->``.
When acquiring mutable access to the represented value, CAF copies the value
before allowing mutable access to it if more than one reference to the value
exists. This copy-on-write optimization avoids race conditions by design, while
minimizing copy operations (see :ref:`copy-on-write`). A mutable reference is
returned from the member functions ``get_mutable()`` and ``move()``. The latter
is a convenience function for ``std::move(x.get_mutable())``. The following
example illustrates how to use ``param<std::string>`` when implementing a simple
dictionary.
.. literalinclude:: /examples/composition/dictionary_behavior.cpp
:language: C++
:lines: 22-44
.. _attach: .. _attach:
...@@ -474,7 +393,8 @@ printing a custom string on exit. ...@@ -474,7 +393,8 @@ printing a custom string on exit.
.. literalinclude:: /examples/broker/simple_broker.cpp .. literalinclude:: /examples/broker/simple_broker.cpp
:language: C++ :language: C++
:lines: 42-47 :start-after: --(rst-attach-begin)--
:end-before: --(rst-attach-end)--
It is possible to attach code to remote actors. However, the cleanup code will It is possible to attach code to remote actors. However, the cleanup code will
run on the local machine. run on the local machine.
...@@ -608,9 +528,8 @@ more efficient code. ...@@ -608,9 +528,8 @@ more efficient code.
).until([&] { return received >= 10; }); ).until([&] { return received >= 10; });
The examples above illustrate the correct usage of the three loops The examples above illustrate the correct usage of the three loops
``receive_for``, ``receive_while`` and ``receive_for``, ``receive_while`` and ``do_receive(...).until``. It is possible
``do_receive(...).until``. It is possible to nest receives and receive to nest receives and receive loops.
loops.
.. code-block:: C++ .. code-block:: C++
...@@ -631,11 +550,10 @@ loops. ...@@ -631,11 +550,10 @@ loops.
Scoped Actors Scoped Actors
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
The class ``scoped_actor`` offers a simple way of communicating with The class ``scoped_actor`` offers a simple way of communicating with CAF actors
CAF actors from non-actor contexts. It overloads ``operator->`` to from non-actor contexts. It overloads ``operator->`` to return a
return a ``blocking_actor*``. Hence, it behaves like the implicit ``blocking_actor*``. Hence, it behaves like the implicit ``self`` pointer in
``self`` pointer in functor-based actors, only that it ceases to exist functor-based actors, only that it ceases to exist at scope end.
at scope end.
.. code-block:: C++ .. code-block:: C++
...@@ -647,4 +565,3 @@ at scope end. ...@@ -647,4 +565,3 @@ at scope end.
// self will be destroyed automatically here; any // self will be destroyed automatically here; any
// actor monitoring it will receive down messages etc. // actor monitoring it will receive down messages etc.
} }
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
Configuring Actor Applications Configuring Actor Applications
============================== ==============================
CAF configures applications at startup using an CAF configures applications at startup using an ``actor_system_config`` or a
``actor_system_config`` or a user-defined subclass of that type. The user-defined subclass of that type. The config objects allow users to add custom
config objects allow users to add custom types, to load modules, and to types, to load modules (optional components), and to fine-tune the behavior of
fine-tune the behavior of loaded modules with command line options or loaded modules with command line options or configuration files (see
configuration files system-config-options_. :ref:`system-config-options`).
The following code example is a minimal CAF application with a :ref:`middleman` The following code example is a minimal CAF application with a :ref:`middleman`
but without any custom configuration options. but without any custom configuration options.
...@@ -30,49 +30,75 @@ The compiler expands this example code to the following. ...@@ -30,49 +30,75 @@ The compiler expands this example code to the following.
return exec_main<io::middleman>(caf_main, argc, argv); return exec_main<io::middleman>(caf_main, argc, argv);
} }
The function ``exec_main`` creates a config object, loads all modules The function ``exec_main`` performs several steps:
requested in ``CAF_MAIN`` and then calls ``caf_main``. A
minimal implementation for ``main`` performing all these steps manually
is shown in the next example for the sake of completeness.
.. code-block:: C++ #. Initialize all meta objects for the type ID blocks listed in ``CAF_MAIN``.
#. Create a config object. If ``caf_main`` has two arguments, then CAF
assumes that the second argument is the configuration and the type gets
derived from that argument. Otherwise, CAF uses ``actor_system_config``.
#. Parse command line arguments and configuration file (if present).
#. Load all modules requested in ``CAF_MAIN``.
#. Create an actor system.
#. Call ``caf_main`` with the actor system and optionally with ``config``.
int main(int argc, char** argv) { When implementing the steps performed by ``CAF_MAIN`` by hand, the ``main``
actor_system_config cfg; function would resemble the following (pseudo) code:
// read CLI options
cfg.parse(argc, argv); .. code-block:: C++
// return immediately if a help text was printed
if (cfg.cli_helptext_printed)
return 0;
// load modules
cfg.load<io::middleman>();
// create actor system and call caf_main
actor_system system{cfg};
caf_main(system);
}
However, setting up config objects by hand is usually not necessary. CAF int main(int argc, char** argv) {
automatically selects user-defined subclasses of // Create the config.
``actor_system_config`` if ``caf_main`` takes a second actor_system_config cfg;
parameter by reference, as shown in the minimal example below. // Add runtime-type information for user-defined types.
cfg.add_message_types<...>();
// Read CLI options.
if (auto err = cfg.parse(argc, argv)) {
std::cerr << "error while parsing CLI and file options: "
<< to_string(err) << std::endl;
return EXIT_FAILURE;
}
// Return immediately if a help text was printed.
if (cfg.cli_helptext_printed)
return EXIT_SUCCESS;
// Load modules.
cfg.load<...>();
// Create the actor system.
actor_system sys{cfg};
// Run user-defined code.
caf_main(sys, cfg);
return EXIT_SUCCESS;
}
Using ``CAF_MAIN`` simply automates that boilerplate code. A minimal example
with a custom type ID block as well as a custom configuration class with the I/O
module loaded looks as follows:
.. code-block:: C++ .. code-block:: C++
class my_config : public actor_system_config { CAF_BEGIN_TYPE_ID_BLOCK(my, first_custom_type_id)
public:
my_config() { // ...
// ...
} CAF_END_TYPE_ID_BLOCK(my)
};
void caf_main(actor_system& system, const my_config& cfg) {
// ...
}
CAF_MAIN() class my_config : public actor_system_config {
public:
my_config() {
// ...
}
};
Users can perform additional initialization, add custom program options, etc. void caf_main(actor_system& system, const my_config& cfg) {
simply by implementing a default constructor. // ...
}
CAF_MAIN(id_block::my, io::middleman)
.. note::
Using type ID blocks is optional. Users can also call ``add_message_type``
for each user-defined type in the constructor of ``my_config``.
.. _system-config-module: .. _system-config-module:
...@@ -110,15 +136,16 @@ object *before* initializing an actor system with it. ...@@ -110,15 +136,16 @@ object *before* initializing an actor system with it.
Command Line Options and INI Configuration Files Command Line Options and INI Configuration Files
------------------------------------------------ ------------------------------------------------
CAF organizes program options in categories and parses CLI arguments as well CAF organizes program options in categories and parses CLI arguments as well as
as INI files. CLI arguments override values in the INI file which override INI files. CLI arguments override values in the INI file which override
hard-coded defaults. Users can add any number of custom program options by hard-coded defaults. Users can add any number of custom program options by
implementing a subtype of ``actor_system_config``. The example below implementing a subtype of ``actor_system_config``. The example below
adds three options to the ``global`` category. adds three options to the ``global`` category.
.. literalinclude:: /examples/remoting/distributed_calculator.cpp .. literalinclude:: /examples/remoting/distributed_calculator.cpp
:language: C++ :language: C++
:lines: 206-218 :start-after: --(rst-config-begin)--
:end-before: --(rst-config-end)--
We create a new ``global`` category in ``custom_options_``. We create a new ``global`` category in ``custom_options_``.
Each following call to ``add`` then appends individual options to the Each following call to ``add`` then appends individual options to the
...@@ -156,23 +183,25 @@ command line. ...@@ -156,23 +183,25 @@ command line.
INI files are organized in categories. No value is allowed outside of a category INI files are organized in categories. No value is allowed outside of a category
(no implicit ``global`` category). The parses uses the following syntax: (no implicit ``global`` category). The parses uses the following syntax:
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key=true`` | is a boolean | | Syntax | Type |
+------------------------+-----------------------------+ +=========================+=======================+
| ``key=1`` | is an integer | | ``key=true`` | Boolean |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key=1.0`` | is an floating point number | | ``key=1`` | Integer |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key=1ms`` | is an timespan | | ``key=1.0`` | Floating point number |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key='foo'`` | is an atom | | ``key=1ms`` | Timespan |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key="foo"`` | is a string | | ``key='foo'`` | Atom |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key=[0, 1, ...]`` | is as a list | | ``key="foo"`` | String |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key={a=1, b=2, ...}``| is a dictionary (map) | | ``key=[0, 1, ...]`` | List |
+------------------------+-----------------------------+ +-------------------------+-----------------------+
| ``key={a=1, b=2, ...}`` | Dictionary (map) |
+-------------------------+-----------------------+
The following example INI file lists all standard options in CAF and their The following example INI file lists all standard options in CAF and their
default value. Note that some options such as ``scheduler.max-threads`` default value. Note that some options such as ``scheduler.max-threads``
...@@ -187,42 +216,66 @@ Adding Custom Message Types ...@@ -187,42 +216,66 @@ Adding Custom Message Types
--------------------------- ---------------------------
CAF requires serialization support for all of its message types (see CAF requires serialization support for all of its message types (see
:ref:`type-inspection`). However, CAF also needs a mapping of unique type names :ref:`type-inspection`). In addition, CAF also needs a mapping of unique type
to user-defined types at runtime. This is required to deserialize arbitrary names to user-defined types at runtime. This is required to deserialize
messages from the network. arbitrary messages from the network.
As an introductory example, we (again) use the following POD type The function ``actor_system_config::add_message_type`` adds runtime-type
``foo``. information for a single type. It takes a template parameter (the message type)
and one function argument (the type name). For example,
``cfg.add_message_type<foo>("foo")`` would add runtime-type information for the
type ``foo``. However, calling ``add_message_type`` for each type individually
is both verbose and prone to error.
For new code, we strongly recommend using the new *type ID blocks*. When setting
the CMake option ``CAF_ENABLE_TYPE_ID_CHECKS`` to ``ON`` (or calling the
``configure`` script with ``--enable-type-id-checks``), CAF raises a static
assertion that prohibits any message type that does not appear in such a type ID
block. When using this API, users can instead call ``add_message_types`` once
per message block. Combined with the type ID checks, this makes sure that the
runtime-type information never runs out of sync when adding new message types.
Passing the type ID blocks to ``CAF_MAIN`` also automates the setup steps for
adding new message types.
A type ID block in CAF starts by calling ``CAF_BEGIN_TYPE_ID_BLOCK``. Inside the
block appear any number of ``CAF_ADD_TYPE_ID`` and ``CAF_ADD_ATOM`` statements.
The type ID block only requires forward declarations. The block ends at
``CAF_END_TYPE_ID_BLOCK``, as shown in the example below.
.. literalinclude:: /examples/custom_type/custom_types_1.cpp .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++ :language: C++
:lines: 24-27 :start-after: --(rst-type-id-block-begin)--
:end-before: --(rst-type-id-block-end)--
To make ``foo`` serializable, we make it inspectable: .. note::
.. literalinclude:: /examples/custom_type/custom_types_1.cpp The second argument to ``CAF_ADD_TYPE_ID`` (the type) must appear in extra
:language: C++ parentheses. This unusual syntax is an artifact of argument handling in
:lines: 30-34 macros. Without the extra set of parentheses, we would not be able to add
types with commas such as ``std::pair``.
Finally, we give ``foo`` a platform-neutral name and add it to the list The first argument to all macros is the name of the type ID block. The macro
of serializable types by using a custom config class. expands a name ``X`` to ``caf::id_block::X``. In the example above, we can refer
to the custom type ID block with ``caf::id_block::custom_types_1``. To add the
required runtime-type information to CAF, we can either call
``cfg.add_message_types<caf::id_block::custom_types_1>()`` on a config object
pass the ID block to ``CAF_MAIN``:
.. literalinclude:: /examples/custom_type/custom_types_1.cpp .. code-block:: C++
:language: C++
:lines: 75-78,81-84 CAF_MAIN(caf::id_block::custom_types_1)
Adding Custom Error Types .. note::
-------------------------
Adding a custom error type to the system is a convenience feature to allow At the point of calling ``CAF_MAIN`` or ``add_message_types``, the compiler
improve the string representation. Error types can be added by implementing a must have the type declaration plus all ``inspect`` overloads available for
render function and passing it to ``add_error_category``, as shown in each type in the type ID block.
:ref:`custom-error`.
.. _add-custom-actor-type: .. _add-custom-actor-type:
Adding Custom Actor Types :sup:`experimental` Adding Custom Actor Types :sup:`experimental`
---------------------------------------------- ---------------------------------------------
Adding actor types to the configuration allows users to spawn actors by their Adding actor types to the configuration allows users to spawn actors by their
name. In particular, this enables spawning of actors on a different node (see name. In particular, this enables spawning of actors on a different node (see
...@@ -231,33 +284,29 @@ simple ``calculator`` actor. ...@@ -231,33 +284,29 @@ simple ``calculator`` actor.
.. literalinclude:: /examples/remoting/remote_spawn.cpp .. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++ :language: C++
:lines: 33-34 :start-after: --(rst-calculator-begin)--
:end-before: --(rst-calculator-end)--
Adding the calculator actor type to our config is achieved by calling Adding the calculator actor type to our config is achieved by calling
``add_actor_type<T>``. Note that adding an actor type in this way ``add_actor_type``. After calling this in our config, we can spawn the
implicitly calls ``add_message_type<T>`` for typed actors ``calculator`` anywhere in the distributed actor system (assuming all nodes use
add-custom-message-type_. This makes our ``calculator`` actor type the same config). Note that the handle type still requires a type ID (see
serializable and also enables remote nodes to spawn calculators anywhere in the :ref:`add-custom-message-type`).
distributed actor system (assuming all nodes use the same config).
.. literalinclude:: /examples/remoting/remote_spawn.cpp After adding the actor type to the config, we can spawn our ``calculator`` by
:language: C++ name. Unlike the regular ``spawn`` overloads, this version requires wrapping the
:lines: 98-109 constructor arguments into a ``message`` and the function might fail and thus
returns an ``expected``:
Our final example illustrates how to spawn a ``calculator`` locally by
using its type name. Because the dynamic type name lookup can fail and the
construction arguments passed as message can mismatch, this version of
``spawn`` returns ``expected<T>``.
.. code-block:: C++ .. code-block:: C++
auto x = system.spawn<calculator>("calculator", make_message()); if (auto x = system.spawn<calculator>("calculator", make_message())) {
if (! x) { // ... do something with *x ...
std::cerr << "*** unable to spawn calculator: " } else {
<< system.render(x.error()) << std::endl; std::cerr << "*** unable to spawn a calculator: " << to_string(x.error())
return; << std::endl;
// ...
} }
calculator c = std::move(*x);
Adding dynamically typed actors to the config is achieved in the same way. When Adding dynamically typed actors to the config is achieved in the same way. When
spawning a dynamically typed actor in this way, the template parameter is spawning a dynamically typed actor in this way, the template parameter is
...@@ -268,25 +317,29 @@ one string is created with: ...@@ -268,25 +317,29 @@ one string is created with:
auto worker = system.spawn<actor>("foo", make_message("bar")); auto worker = system.spawn<actor>("foo", make_message("bar"));
Because constructor (or function) arguments for spawning the actor are stored Because constructor (or function) arguments for spawning the actor are stored in
in a ``message``, only actors with appropriate input types are allowed. a ``message``, only actors with appropriate input types are allowed. Pointer
For example, pointer types are illegal. Hence users need to replace C-strings types, for example, are illegal in messages.
with ``std::string``.
.. _log-output: .. _log-output:
Log Output Log Output
---------- ----------
Logging is disabled in CAF per default. It can be enabled by setting the CAF comes with a logger integrated into the actor system. By default, CAF itself
``--with-log-level=`` option of the ``configure`` script to one won't emit any log messages. Developers can set the verbosity of CAF itself at
of ``error``, ``warning``, ``info``, ``debug``, build time by setting the CMake option ``CAF_LOG_LEVEL`` manually or by passing
or ``trace`` (from least output to most). Alternatively, setting the ``--with-log-level=...`` to the ``configure`` script. The available verbosity
CMake variable ``CAF_LOG_LEVEL`` to one of these values has the same levels are (from least to most output):
effect.
- ``error``
- ``warning``
- ``info``
- ``debug``
- ``trace``
All logger-related configuration options listed here and in The logging infrastructure is always available to users, regardless of the
system-config-options_ are silently ignored if logging is disabled. verbosity level of CAF itself.
.. _log-output-file-name: .. _log-output-file-name:
...@@ -296,25 +349,25 @@ File Name ...@@ -296,25 +349,25 @@ File Name
The output file is generated from the template configured by The output file is generated from the template configured by
``logger-file-name``. This template supports the following variables. ``logger-file-name``. This template supports the following variables.
+----------------+--------------------------------+ +-----------------+--------------------------------+
| **Variable** | **Output** | | Variable | Output |
+----------------+--------------------------------+ +=================+================================+
| ``[PID]`` | The OS-specific process ID. | | ``[PID]`` | The OS-specific process ID. |
+----------------+--------------------------------+ +-----------------+--------------------------------+
| ``[TIMESTAMP]``| The UNIX timestamp on startup. | | ``[TIMESTAMP]`` | The UNIX timestamp on startup. |
+----------------+--------------------------------+ +-----------------+--------------------------------+
| ``[NODE]`` | The node ID of the CAF system. | | ``[NODE]`` | The node ID of the CAF system. |
+----------------+--------------------------------+ +-----------------+--------------------------------+
.. _log-output-console: .. _log-output-console:
Console Console
~~~~~~~ ~~~~~~~
Console output is disabled per default. Setting ``logger-console`` to Console output is disabled per default. Setting ``logger-console`` to either
either ``"uncolored"`` or ``"colored"`` prints log events to ``uncolored`` or ``colored`` prints log events to ``std::clog``. Using the
``std::clog``. Using the ``"colored"`` option will print the ``colored`` option will print the log events in different colors depending on
log events in different colors depending on the severity level. the severity level.
.. _log-output-format-strings: .. _log-output-format-strings:
...@@ -326,35 +379,35 @@ events via ``logger-file-format`` and ...@@ -326,35 +379,35 @@ events via ``logger-file-format`` and
``logger-console-format``. Note that format modifiers are not supported ``logger-console-format``. Note that format modifiers are not supported
at the moment. The recognized field identifiers are: at the moment. The recognized field identifiers are:
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| **Character**| **Output** | | Pattern | Output |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +=========+=======================================================================================================================+
| ``c`` | The category/component. | | ``%c`` | The category/component. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``C`` | The full qualifier of the current function. For example, the qualifier of ``void ns::foo::bar()`` is printed as ``ns.foo``. | | ``%C`` | The full qualifier of the current function. For example, the function ``void ns::foo::bar()`` would print ``ns.foo``. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``d`` | The date in ISO 8601 format, i.e., ``"YYYY-MM-DDThh:mm:ss"``. | | ``%d`` | The date in ISO 8601 format, i.e., ``"YYYY-MM-DDThh:mm:ss"``. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``F`` | The file name. | | ``%F`` | The file name. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``L`` | The line number. | | ``%L`` | The line number. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``m`` | The user-defined log message. | | ``%m`` | The user-defined log message. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``M`` | The name of the current function. For example, the name of ``void ns::foo::bar()`` is printed as ``bar``. | | ``%M`` | The name of the current function. For example, the name of ``void ns::foo::bar()`` is printed as ``bar``. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``n`` | A newline. | | ``%n`` | A newline. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``p`` | The priority (severity level). | | ``%p`` | The priority (severity level). |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``r`` | Elapsed time since starting the application in milliseconds. | | ``%r`` | Elapsed time since starting the application in milliseconds. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``t`` | ID of the current thread. | | ``%t`` | ID of the current thread. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``a`` | ID of the current actor (or ``actor0`` when not logging inside an actor). | | ``%a`` | ID of the current actor (or ``actor0`` when not logging inside an actor). |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%`` | A single percent sign. | | ``%%`` | A single percent sign. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+ +---------+-----------------------------------------------------------------------------------------------------------------------+
.. _log-output-filtering: .. _log-output-filtering:
......
...@@ -12,55 +12,63 @@ include a message to provide additional context information. ...@@ -12,55 +12,63 @@ include a message to provide additional context information.
Class Interface Class Interface
--------------- ---------------
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| **Constructors** | | | **Constructors** |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``(Enum x)`` | Construct error by calling ``make_error(x)`` | | ``(Enum x)`` | Construct error by calling ``make_error(x)`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` | | ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y, message z)``| Construct error with code ``x``, category ``y``, and context ``z`` | | ``(uint8_t x, atom_value y, message z)`` | Construct error with code ``x``, category ``y``, and context ``z`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| | | | **Observers** |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| **Observers** | | | ``uint8_t code()`` | Returns the error code |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code | | ``atom_value category()`` | Returns the error category |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``atom_value category()`` | Returns the error category | | ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information | | ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+
.. _custom-error: .. _custom-error:
Add Custom Error Categories Add Custom Error Categories
--------------------------- ---------------------------
Adding custom error categories requires three steps: (1) declare an enum class Adding custom error categories requires two steps: (1) declare an enum class of
of type ``uint8_t`` with the first value starting at 1, (2) specialize type ``uint8_t`` with the first value starting at 1, (2) enable conversions from
``error_category`` to give your type a custom ID (value 0-99 are the error code enum to ``error`` by using the macro ``CAF_ERROR_CODE_ENUM``. The
reserved by CAF), and (3) add your error category to the actor system config. following example illustrates these two steps with a ``math_error`` enum to
The following example adds custom error codes for math errors. signal divisions by zero:
.. literalinclude:: /examples/message_passing/divider.cpp .. literalinclude:: /examples/message_passing/divider.cpp
:language: C++ :language: C++
:lines: 17-47 :start-after: --(rst-divider-begin)--
:end-before: --(rst-divider-end)--
.. note::
CAF stores the integer value with an atom that disambiguates error codes from
different categories. The macro ``CAF_ERROR_CODE_ENUM`` uses the given type
name as atom as well. This means the same limitations apply. In particular,
10 characters or less. If the type name exceeds this limit, users can pass
two arguments instead. In the example above, we could call
``CAF_ERROR_CODE_ENUM(math_error, "math")`` instead.
.. _sec: .. _sec:
System Error Codes System Error Codes
------------------ ------------------
System Error Codes (SECs) use the error category ``"system"``. They System Error Codes (SECs) represent errors in the actor system or one of its
represent errors in the actor system or one of its modules and are defined as modules and are defined as follows:
follows.
.. literalinclude:: /libcaf_core/caf/sec.hpp .. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++ :language: C++
:lines: 32-117 :start-after: --(rst-sec-begin)--
:end-before: --(rst-sec-end)--
.. _exit-reason: .. _exit-reason:
...@@ -77,5 +85,5 @@ in the same way. The latter terminates an actor unconditionally when used in ...@@ -77,5 +85,5 @@ in the same way. The latter terminates an actor unconditionally when used in
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp .. literalinclude:: /libcaf_core/caf/exit_reason.hpp
:language: C++ :language: C++
:lines: 29-49 :start-after: --(rst-exit-reason-begin)--
:end-before: --(rst-exit-reason-end)--
...@@ -14,8 +14,8 @@ name, joining, and leaving. ...@@ -14,8 +14,8 @@ name, joining, and leaving.
std::string id = "foo"; std::string id = "foo";
auto expected_grp = system.groups().get(module, id); auto expected_grp = system.groups().get(module, id);
if (! expected_grp) { if (! expected_grp) {
std::cerr << "*** cannot load group: " std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
<< system.render(expected_grp.error()) << std::endl; << std::endl;
return; return;
} }
auto grp = std::move(*expected_grp); auto grp = std::move(*expected_grp);
......
...@@ -45,10 +45,10 @@ Dynamically Typed Actor ...@@ -45,10 +45,10 @@ Dynamically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
A dynamically typed actor accepts any kind of message and dispatches on its A dynamically typed actor accepts any kind of message and dispatches on its
content dynamically at the receiver. This is the "traditional" messaging content dynamically at the receiver. This is the traditional messaging style
style found in implementations like Erlang or Akka. The upside of this approach found in implementations like Erlang or Akka. The upside of this approach is
is (usually) faster prototyping and less code. This comes at the cost of (usually) faster prototyping and less code. This comes at the cost of requiring
requiring excessive testing. excessive testing.
Statically Typed Actor Statically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -85,10 +85,9 @@ This policy models split/join or scatter/gather work flows, where a work item ...@@ -85,10 +85,9 @@ 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 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. results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together The join function is responsible for ``glueing`` all result messages together to
to create a single result. The function is called with the result object create a single result. The function is called with the result object (initialed
(initialed using ``init``) and the current result messages from a using ``init``) and the current result messages from a worker.
worker.
The first argument of a split function is a mapping from actors (workers) to 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 tasks (messages). The second argument is the input message. The default split
......
...@@ -19,7 +19,7 @@ change its behavior when not receiving message after a certain amount of time. ...@@ -19,7 +19,7 @@ change its behavior when not receiving message after a certain amount of time.
message_handler x1{ message_handler x1{
[](int i) { /*...*/ }, [](int i) { /*...*/ },
[](double db) { /*...*/ }, [](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ } [](int a, int b, int c) { /*...*/ },
}; };
In our first example, ``x1`` models a behavior accepting messages that consist In our first example, ``x1`` models a behavior accepting messages that consist
...@@ -31,7 +31,7 @@ other message is not matched and gets forwarded to the default handler (see ...@@ -31,7 +31,7 @@ other message is not matched and gets forwarded to the default handler (see
message_handler x2{ message_handler x2{
[](double db) { /*...*/ }, [](double db) { /*...*/ },
[](double db) { /* - unreachable - */ } [](double db) { /* - unreachable - */ },
}; };
Our second example illustrates an important characteristic of the matching Our second example illustrates an important characteristic of the matching
...@@ -55,37 +55,54 @@ The handler ``x4`` will consume messages with a single ...@@ -55,37 +55,54 @@ The handler ``x4`` will consume messages with a single
``double`` using the first callback in ``x2``, essentially ``double`` using the first callback in ``x2``, essentially
overriding the second callback in ``x1``. overriding the second callback in ``x1``.
.. code-block:: C++
behavior bhvr{
[](int i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ },
after(std::chrono::seconds(10)) >> [] { /*..*/ },
};
Similar to ``message_handler``, ``behavior`` stores a set of functions.
Additionally, behaviors accept optional timeouts as *last* argument to the
constructor. In the example above, the behavior calls the timeout handler after
receiving no messages for 10 seconds. This timeout triggers repeatedly, i.e.,
the timeout handler gets called again after 20 seconds when not receiving any
messages, then again after 30 seconds, and so on.
.. _atom: .. _atom:
Atoms Atoms
----- -----
Defining message handlers in terms of callbacks is convenient, but requires a Defining message handlers in terms of callbacks is convenient, but requires a
simple way to annotate messages with meta data. Imagine an actor that provides simple way to annotate messages with meta data. Imagine an actor that provides a
a mathematical service for integers. It receives two integers, performs a mathematical service for integers. It receives two integers, performs a
user-defined operation and returns the result. Without additional context, the user-defined operation and returns the result. Without additional context, the
actor cannot decide whether it should multiply or add the integers. Thus, the actor cannot decide whether it should multiply or add the integers, for example.
operation must be encoded into the message. The Erlang programming language
introduced an approach to use non-numerical constants, so-called Thus, the operation must be encoded into the message. The Erlang programming
*atoms*, which have an unambiguous, special-purpose type and do not have language introduced an approach to use non-numerical constants, so-called
the runtime overhead of string constants. *atoms*, which have an unambiguous, special-purpose type and do not have the
runtime overhead of string constants.
Atoms in CAF are mapped to integer values at compile time. This mapping is Atoms in CAF are mapped to integer values at compile time. This mapping is
guaranteed to be collision-free and invertible, but limits atom literals to ten guaranteed to be collision-free and invertible, but limits atom literals to ten
characters and prohibits special characters. Legal characters are characters and prohibits special characters. Legal characters are ``_0-9A-Za-z``
``_0-9A-Za-z`` and the whitespace character. Atoms are created using and the whitespace character. Atoms are created using the ``constexpr`` function
the ``constexpr`` function ``atom``, as the following example ``atom``, as the following example illustrates.
illustrates.
.. code-block:: C++ .. code-block:: C++
atom_value a1 = atom("add"); atom_value a1 = atom("add");
atom_value a2 = atom("multiply"); atom_value a2 = atom("multiply");
**Warning**: The compiler cannot enforce the restrictions at compile time, .. warning::
except for a length check. The assertion ``atom("!?") != atom("?!")``
is not true, because each invalid character translates to a whitespace The compiler cannot enforce the restrictions at compile time, except for a
character. length check. The assertion ``atom("!?") != atom("?!")`` is not true, because
each invalid character translates to a whitespace character.
While the ``atom_value`` is computed at compile time, it is not While the ``atom_value`` is computed at compile time, it is not
uniquely typed and thus cannot be used in the signature of a callback. To uniquely typed and thus cannot be used in the signature of a callback. To
......
...@@ -4,12 +4,11 @@ Message Passing ...@@ -4,12 +4,11 @@ Message Passing
=============== ===============
Message passing in CAF is always asynchronous. Further, CAF neither guarantees Message passing in CAF is always asynchronous. Further, CAF neither guarantees
message delivery nor message ordering in a distributed setting. CAF uses TCP message delivery nor message ordering in a distributed setting. CAF uses TCP per
per default, but also enables nodes to send messages to other nodes without default, but also enables nodes to send messages to other nodes without having a
having a direct connection. In this case, messages are forwarded by direct connection. In this case, messages are forwarded by intermediate nodes
intermediate nodes and can get lost if one of the forwarding nodes fails. and can get lost if one of the forwarding nodes fails. Likewise, forwarding
Likewise, forwarding paths can change dynamically and thus cause messages to paths can change dynamically and thus cause messages to arrive out of order.
arrive out of order.
The messaging layer of CAF has three primitives for sending messages: ``send``, The messaging layer of CAF has three primitives for sending messages: ``send``,
``request``, and ``delegate``. The former simply enqueues a message to the ``request``, and ``delegate``. The former simply enqueues a message to the
...@@ -22,8 +21,8 @@ Structure of Mailbox Elements ...@@ -22,8 +21,8 @@ Structure of Mailbox Elements
----------------------------- -----------------------------
When enqueuing a message to the mailbox of an actor, CAF wraps the content of When enqueuing a message to the mailbox of an actor, CAF wraps the content of
the message into a ``mailbox_element`` (shown below) to add meta data the message into a ``mailbox_element`` (shown below) to add meta data and
and processing paths. processing paths.
.. _mailbox_element: .. _mailbox_element:
...@@ -69,11 +68,6 @@ Message types in CAF must meet the following requirements: ...@@ -69,11 +68,6 @@ Message types in CAF must meet the following requirements:
2. Default constructible 2. Default constructible
3. Copy constructible 3. Copy constructible
A type is serializable if it provides free function
``serialize(Serializer&, T&)`` or
``serialize(Serializer&, T&, const unsigned int)``. Accordingly, a type is
inspectable if it provides a free function ``inspect(Inspector&, T&)``.
Requirement 2 is a consequence of requirement 1, because CAF needs to be able to Requirement 2 is a consequence of requirement 1, because CAF needs to be able to
create an object of a type before it can call ``serialize`` or ``inspect`` on create an object of a type before it can call ``serialize`` or ``inspect`` on
it. Requirement 3 allows CAF to implement Copy on Write (see it. Requirement 3 allows CAF to implement Copy on Write (see
...@@ -84,8 +78,8 @@ it. Requirement 3 allows CAF to implement Copy on Write (see ...@@ -84,8 +78,8 @@ it. Requirement 3 allows CAF to implement Copy on Write (see
Default and System Message Handlers Default and System Message Handlers
----------------------------------- -----------------------------------
CAF has three system-level message types (``down_msg``, ``exit_msg``, and CAF has a couple of system-level message types such as ``down_msg`` and
``error``) that all actor should handle regardless of there current state. ``exit_msg`` that all actor should handle regardless of there current state.
Consequently, event-based actors handle such messages in special-purpose message Consequently, event-based actors handle such messages in special-purpose message
handlers. Additionally, event-based actors have a fallback handler for unmatched handlers. Additionally, event-based actors have a fallback handler for unmatched
messages. Note that blocking actors have neither of those special-purpose messages. Note that blocking actors have neither of those special-purpose
...@@ -116,8 +110,8 @@ after receiving an ``exit_msg`` unless the exit reason is ...@@ -116,8 +110,8 @@ after receiving an ``exit_msg`` unless the exit reason is
``exit_reason::normal``. This mechanism propagates failure states in an actor ``exit_reason::normal``. This mechanism propagates failure states in an actor
system. Linked actors form a sub system in which an error causes all actors to system. Linked actors form a sub system in which an error causes all actors to
fail collectively. Actors can override the default handler via fail collectively. Actors can override the default handler via
``set_exit_handler(f)``, where ``f`` is a function object with signature ``set_exit_handler(f)``, where ``f`` is a function object with signature ``void
``void (exit_message&)`` or ``void (scheduled_actor*, exit_message&)``. (exit_message&)`` or ``void (scheduled_actor*, exit_message&)``.
.. _error-message: .. _error-message:
...@@ -130,8 +124,8 @@ usually cause the receiving actor to terminate, unless a custom handler was ...@@ -130,8 +124,8 @@ usually cause the receiving actor to terminate, unless a custom handler was
installed via ``set_error_handler(f)``, where ``f`` is a function object with installed via ``set_error_handler(f)``, where ``f`` is a function object with
signature ``void (error&)`` or ``void (scheduled_actor*, error&)``. signature ``void (error&)`` or ``void (scheduled_actor*, error&)``.
Additionally, ``request`` accepts an error handler as second argument to handle Additionally, ``request`` accepts an error handler as second argument to handle
errors for a particular request. The default handler is used as fallback if errors for a particular request (see :ref:`error-response`). The default handler
``request`` is used without error handler. is used as fallback if ``request`` is used without error handler.
.. _default-handler: .. _default-handler:
...@@ -170,53 +164,48 @@ and to provide a convenient API for this style of communication. ...@@ -170,53 +164,48 @@ and to provide a convenient API for this style of communication.
Sending Requests and Handling Responses Sending Requests and Handling Responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Actors send request messages by calling ``request(receiver, timeout, Actors send request messages by calling
content...)``. This function returns an intermediate object that allows an actor ``request(receiver, timeout, content...)``. This function returns an
to set a one-shot handler for the response message. Event-based actors can use intermediate object that allows an actor to set a one-shot handler for the
either ``request(...).then`` or ``request(...).await``. The former multiplexes response message.
the one-shot handler with the regular actor behavior and handles requests as
they arrive. The latter suspends the regular actor behavior until all awaited
responses arrive and handles requests in LIFO order. Blocking actors always use
``request(...).receive``, which blocks until the one-shot handler was called.
Actors receive a ``sec::request_timeout`` (see :ref:`sec`) error message (see
:ref:`error-message`) if a timeout occurs. Users can set the timeout to
``infinite`` for unbound operations. This is only recommended if the receiver is
running locally.
In our following example, we use the simple cell actors shown below as
communication endpoints.
.. literalinclude:: /examples/message_passing/request.cpp Event-based actors can use either ``request(...).then`` or
:language: C++ ``request(...).await``. The former multiplexes the one-shot handler with the
:lines: 20-37 regular actor behavior and handles requests as they arrive. The latter suspends
the regular actor behavior until all awaited responses arrive and handles
requests in LIFO order.
The first part of the example illustrates how event-based actors can use either Blocking actors always use ``request(...).receive``, which blocks until the
``then`` or ``await``. one-shot handler was called. Actors receive a ``sec::request_timeout`` (see
:ref:`sec`) error message (see :ref:`error-message`) if a timeout occurs. Users
can set the timeout to ``infinite`` for unbound operations. This is only
recommended if the receiver is known to run locally.
.. literalinclude:: /examples/message_passing/request.cpp In our following example, we use the simple cell actors shown below as
:language: C++ communication endpoints. The first part of the example illustrates how
:lines: 39-51 event-based actors can use either ``then`` or ``await``. The second half of the
example shows a blocking actor making use of ``receive``. Note that blocking
actors have no special-purpose handler for error messages and therefore are
required to pass a callback for error messages when handling response messages.
The second half of the example shows a blocking actor making use of
``receive``. Note that blocking actors have no special-purpose handler
for error messages and therefore are required to pass a callback for error
messages when handling response messages.
.. literalinclude:: /examples/message_passing/request.cpp .. literalinclude:: /examples/message_passing/request.cpp
:language: C++ :language: C++
:lines: 53-64 :start-after: --(rst-cell-begin)--
:end-before: --(rst-cell-end)--
We spawn five cells and assign the values 0, 1, 4, 9, and 16. In our example, we spawn five cells and assign the values 0, 1, 4, 9, and 16:
.. literalinclude:: /examples/message_passing/request.cpp .. literalinclude:: /examples/message_passing/request.cpp
:language: C++ :language: C++
:lines: 67-69 :start-after: --(rst-spawn-begin)--
:end-before: --(rst-spawn-end)--
When passing the ``cells`` vector to our three different When passing the ``cells`` vector to our three different ``testee``
implementations, we observe three outputs. Our ``waiting_testee`` actor implementations, we observe three outputs. Our ``waiting_testee`` actor will
will always print: always print:
.. :: .. code-block:: none
cell #9 -> 16 cell #9 -> 16
cell #8 -> 9 cell #8 -> 9
...@@ -233,7 +222,7 @@ immediately. ...@@ -233,7 +222,7 @@ immediately.
Finally, the ``blocking_testee`` implementation will always print: Finally, the ``blocking_testee`` implementation will always print:
.. :: .. code-block:: none
cell #5 -> 0 cell #5 -> 0
cell #6 -> 1 cell #6 -> 1
...@@ -243,7 +232,9 @@ Finally, the ``blocking_testee`` implementation will always print: ...@@ -243,7 +232,9 @@ Finally, the ``blocking_testee`` implementation will always print:
Both event-based approaches send all requests, install a series of one-shot Both event-based approaches send all requests, install a series of one-shot
handlers, and then return from the implementing function. In contrast, the handlers, and then return from the implementing function. In contrast, the
blocking function waits for a response before sending another request. blocking function waits for a response before sending the next request.
.. _error-response:
Error Handling in Requests Error Handling in Requests
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -252,22 +243,23 @@ Requests allow CAF to unambiguously correlate request and response messages. ...@@ -252,22 +243,23 @@ Requests allow CAF to unambiguously correlate request and response messages.
This is also true if the response is an error message. Hence, CAF allows to add This is also true if the response is an error message. Hence, CAF allows to add
an error handler as optional second parameter to ``then`` and ``await`` (this an error handler as optional second parameter to ``then`` and ``await`` (this
parameter is mandatory for ``receive``). If no such handler is defined, the parameter is mandatory for ``receive``). If no such handler is defined, the
default error handler (see :ref:`error-message`) is used as a fallback in default error handler (see :ref:`error-message`) get called instead.
scheduled actors.
As an example, we consider a simple divider that returns an error on a division As an example, we consider a simple divider that returns an error on a division
by zero. This examples uses a custom error category (see :ref:`error`). by zero. This examples uses a custom error category (see :ref:`error`).
.. literalinclude:: /examples/message_passing/divider.cpp .. literalinclude:: /examples/message_passing/divider.cpp
:language: C++ :language: C++
:lines: 17-19,49-59 :start-after: --(rst-divider-begin)--
:end-before: --(rst-divider-end)--
When sending requests to the divider, we use a custom error handlers to report When sending requests to the divider, we use a custom error handlers to report
errors to the user. errors to the user.
.. literalinclude:: /examples/message_passing/divider.cpp .. literalinclude:: /examples/message_passing/divider.cpp
:language: C++ :language: C++
:lines: 70-76 :start-after: --(rst-request-begin)--
:end-before: --(rst-request-end)--
.. _delay-message: .. _delay-message:
...@@ -279,37 +271,38 @@ illustrated in the following time-based loop example. ...@@ -279,37 +271,38 @@ illustrated in the following time-based loop example.
.. literalinclude:: /examples/message_passing/dancing_kirby.cpp .. literalinclude:: /examples/message_passing/dancing_kirby.cpp
:language: C++ :language: C++
:lines: 58-75 :start-after: --(rst-dancing-kirby-begin)--
:end-before: --(rst-dancing-kirby-end)--
.. _delegate: .. _delegate:
Delegating Messages Delegating Messages
------------------- -------------------
Actors can transfer responsibility for a request by using ``delegate``. Actors can transfer responsibility for a request by using ``delegate``. This
This enables the receiver of the delegated message to reply as usual---simply enables the receiver of the delegated message to reply as usual (by returning a
by returning a value from its message handler---and the original sender of the value from its message handler) and the original sender of the message will
message will receive the response. The following diagram illustrates request receive the response. The following diagram illustrates request delegation from
delegation from actor B to actor C. actor B to actor C.
.. :: .. code-block::
A B C A B C
| | | | | |
| ---(request)---> | | | ---(request)---> | |
| | ---(delegate)--> | | | ---(delegate)--> |
| X |---\ | X |---\
| | | compute | | | compute
| | | result | | | result
| |<--/ | |<--/
| <-------------(reply)-------------- | | <-------------(reply)-------------- |
| X | X
|---\ |---\
| | handle | | handle
| | response | | response
|<--/ |<--/
| |
X X
Returning the result of ``delegate(...)`` from a message handler, as Returning the result of ``delegate(...)`` from a message handler, as
shown in the example below, suppresses the implicit response message and allows shown in the example below, suppresses the implicit response message and allows
...@@ -317,7 +310,8 @@ the compiler to check the result type when using statically typed actors. ...@@ -317,7 +310,8 @@ the compiler to check the result type when using statically typed actors.
.. literalinclude:: /examples/message_passing/delegating.cpp .. literalinclude:: /examples/message_passing/delegating.cpp
:language: C++ :language: C++
:lines: 15-36 :start-after: --(rst-delegate-begin)--
:end-before: --(rst-delegate-end)--
.. _promise: .. _promise:
...@@ -334,14 +328,14 @@ a promise, an actor can fulfill it by calling the member function ...@@ -334,14 +328,14 @@ a promise, an actor can fulfill it by calling the member function
.. literalinclude:: /examples/message_passing/promises.cpp .. literalinclude:: /examples/message_passing/promises.cpp
:language: C++ :language: C++
:lines: 18-43 :start-after: --(rst-promises-begin)--
:end-before: --(rst-promises-end)--
Message Priorities Message Priorities
------------------ ------------------
By default, all messages have the default priority, i.e., By default, all messages have the default priority, i.e.,
``message_priority::normal``. Actors can send urgent messages by ``message_priority::normal``. Actors can send urgent messages by setting the
setting the priority explicitly: priority explicitly: ``send<message_priority::high>(dst,...)``. Urgent messages
``send<message_priority::high>(dst,...)``. Urgent messages are put into are put into a different queue of the receiver's mailbox to avoid long delays
a different queue of the receiver's mailbox. Hence, long wait delays can be for urgent communication.
avoided for urgent communication.
...@@ -92,12 +92,10 @@ connect to the published actor by calling ``remote_actor``: ...@@ -92,12 +92,10 @@ connect to the published actor by calling ``remote_actor``:
// node B // node B
auto ping = system.middleman().remote_actor("node A", 4242); auto ping = system.middleman().remote_actor("node A", 4242);
if (! ping) { if (!ping)
cerr << "unable to connect to node A: " cerr << "unable to connect to node A: " << to_string(ping.error()) << '\n';
<< system.render(ping.error()) << std::endl; else
} else {
self->send(*ping, ping_atom::value); self->send(*ping, ping_atom::value);
}
There is no difference between server and client after the connection phase. There is no difference between server and client after the connection phase.
Remote actors use the same handle types as local actors and are thus fully Remote actors use the same handle types as local actors and are thus fully
......
...@@ -4,23 +4,32 @@ Overview ...@@ -4,23 +4,32 @@ Overview
Compiling CAF requires CMake and a C++11-compatible compiler. To get and Compiling CAF requires CMake and a C++11-compatible compiler. To get and
compile the sources on UNIX-like systems, type the following in a terminal: compile the sources on UNIX-like systems, type the following in a terminal:
.. :: .. code-block:: none
git clone https://github.com/actor-framework/actor-framework git clone https://github.com/actor-framework/actor-framework
cd actor-framework cd actor-framework
./configure ./configure
make make -C build
make install [as root, optional] make -C build test [optional]
make -C build install [as root, optional]
We recommended to run the unit tests as well: If the output of ``make test`` indicates an error, 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``.
.. :: The configure script provides several build options for advanced configuration.
Please run ``./configure -h`` to see all available options. If you are building
CAF only as a dependency, disabling the unit tests and the examples can safe you
some time during the build.
make test .. note::
If the output indicates an error, please submit a bug report that includes (a) The ``configure`` script provides a convenient way for creating a build
your compiler version, (b) your OS, and (c) the content of the file directory and calling CMake. Users that are familiar with CMake can of course
``build/Testing/Temporary/LastTest.log``. also use CMake directly and avoid using the ``configure`` script entirely.
On Windows, we recomment using the CMake GUI to generate a Visual Studio
project file for CAF.
Features Features
-------- --------
......
...@@ -19,32 +19,28 @@ It is worth mentioning that the registry is not synchronized between connected ...@@ -19,32 +19,28 @@ It is worth mentioning that the registry is not synchronized between connected
actor system. Each actor system has its own, local registry in a distributed actor system. Each actor system has its own, local registry in a distributed
setting. setting.
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| **Types** | | | **Types** |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``name_map`` | ``unordered_map<atom_value, strong_actor_ptr>`` | | ``name_map`` | ``unordered_map<atom_value, strong_actor_ptr>`` |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| | | | **Observers** |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| **Observers** | | | ``strong_actor_ptr get(actor_id)`` | Returns the actor associated to given ID. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``strong_actor_ptr get(actor_id)`` | Returns the actor associated to given ID. | | ``strong_actor_ptr get(atom_value)`` | Returns the actor associated to given name. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``strong_actor_ptr get(atom_value)`` | Returns the actor associated to given name. | | ``name_map named_actors()`` | Returns all name mappings. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``name_map named_actors()`` | Returns all name mappings. | | ``size_t running()`` | Returns the number of currently running actors. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``size_t running()`` | Returns the number of currently running actors. | | **Modifiers** |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| | | | ``put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| **Modifiers** | | | ``erase(actor_id)`` | Removes an ID mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``void put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. | | ``put(atom_value, strong_actor_ptr)`` | Maps an actor to a name. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``void erase(actor_id)`` | Removes an ID mapping from the registry. | | ``erase(atom_value)`` | Removes a name mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``void put(atom_value, strong_actor_ptr)``| Maps an actor to a name. |
+-------------------------------------------+-------------------------------------------------+
| ``void erase(atom_value)`` | Removes a name mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+
...@@ -10,7 +10,8 @@ named "calculator". ...@@ -10,7 +10,8 @@ named "calculator".
.. literalinclude:: /examples/remoting/remote_spawn.cpp .. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++ :language: C++
:lines: 123-137 :start-after: --(rst-client-begin)--
:end-before: --(rst-client-end)--
We first connect to a CAF node with ``middleman().connect(...)``. On success, We first connect to a CAF node with ``middleman().connect(...)``. On success,
``connect`` returns the node ID we need for ``remote_spawn``. This requires the ``connect`` returns the node ID we need for ``remote_spawn``. This requires the
......
...@@ -69,48 +69,50 @@ Defining Sources ...@@ -69,48 +69,50 @@ Defining Sources
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 17-48 :start-after: --(rst-source-begin)--
:end-before: --(rst-source-end)--
The simplest way to defining a source is to use the The simplest way to defining a source is to use the ``attach_stream_source``
``attach_stream_source`` function and pass it four arguments: a pointer function and pass it four arguments: a pointer to *self*, *initializer* for the
to *self*, *initializer* for the state, *generator* for state, *generator* for producing values, and *predicate* for signaling the end
producing values, and *predicate* for signaling the end of the stream. of the stream.
Defining Stages Defining Stages
--------------- ---------------
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 50-83 :start-after: --(rst-stage-begin)--
:end-before: --(rst-stage-end)--
The function ``make_stage`` also takes three lambdas but additionally The function ``make_stage`` also takes three lambdas but additionally the
the received input stream handshake as first argument. Instead of a predicate, received input stream handshake as first argument. Instead of a predicate,
``make_stage`` only takes a finalizer, since the stage does not produce ``make_stage`` only takes a finalizer, since the stage does not produce data on
data on its own and a stream terminates if no more sources exist. its own and a stream terminates if no more sources exist.
Defining Sinks Defining Sinks
-------------- --------------
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 85-114 :start-after: --(rst-sink-begin)--
:end-before: --(rst-sink-end)--
The function ``make_sink`` is similar to ``make_stage``, except The function ``make_sink`` is similar to ``make_stage``, except that is does not
that is does not produce outputs. produce outputs.
Initiating Streams Initiating Streams
------------------ ------------------
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 128-132 :start-after: --(rst-main-begin)--
:end-before: --(rst-main-end)--
In our example, we always have a source ``int_source`` and a sink In our example, we always have a source ``int_source`` and a sink ``int_sink``
``int_sink`` with an optional stage ``int_selector``. Sending with an optional stage ``int_selector``. Sending ``open_atom`` to the source
``open_atom`` to the source initiates the stream and the source will initiates the stream and the source will respond with a stream handshake.
respond with a stream handshake.
Using the actor composition in CAF (``snk * src`` reads *sink Using the actor composition in CAF (``snk * src`` reads *sink after source*)
after source*) allows us to redirect the stream handshake we send in allows us to redirect the stream handshake we send in ``caf_main`` to the sink
``caf_main`` to the sink (or to the stage and then from the stage to (or to the stage and then from the stage to the sink).
the sink).
...@@ -99,7 +99,7 @@ as ``CAF_CHECK`` and provide tests rather than implementing a ...@@ -99,7 +99,7 @@ as ``CAF_CHECK`` and provide tests rather than implementing a
}, },
[&](caf::error& err) { [&](caf::error& err) {
// Must not happen, stop test. // Must not happen, stop test.
CAF_FAIL(sys.render(err)); CAF_FAIL(err);
}); });
} }
...@@ -127,18 +127,16 @@ configuration classes. ...@@ -127,18 +127,16 @@ configuration classes.
Using this fixture unlocks three additional macros: Using this fixture unlocks three additional macros:
* ``expect`` checks for a single message. The macro verifies the content types of the message and invokes the necessary member functions on the test coordinator. Optionally, the macro checks the receiver of the message and its content. If the expected message does not exist, the test aborts. - ``expect`` checks for a single message. The macro verifies the content types of the message and invokes the necessary member functions on the test coordinator. Optionally, the macro checks the receiver of the message and its content. If the expected message does not exist, the test aborts.
* ``allow`` is similar to ``expect``, but it does not abort the test if the expected message is missing. This macro returns ``true`` if the allowed message was delivered, ``false`` otherwise. - ``allow`` is similar to ``expect``, but it does not abort the test if the expected message is missing. This macro returns ``true`` if the allowed message was delivered, ``false`` otherwise.
* ``disallow`` aborts the test if a particular message was delivered to an actor. - ``disallow`` aborts the test if a particular message was delivered to an actor.
The following example implements two actors, ``ping`` and The following example implements two actors, ``ping`` and ``pong``, that
``pong``, that exchange a configurable amount of messages. The test exchange a configurable amount of messages. The test *three pings* then checks
*three pings* then checks the contents of each message with the contents of each message with ``expect`` and verifies that no additional
``expect`` and verifies that no additional messages exist using messages exist using ``disallow``.
``disallow``.
.. literalinclude:: /examples/testing/ping_pong.cpp .. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++ :language: C++
:lines: 12-60 :start-after: --(rst-ping-pong-begin)--
:end-before: --(rst-ping-pong-end)--
.. _type-inspection: .. _type-inspection:
Type Inspection (Serialization and String Conversion) Type Inspection
===================================================== ===============
CAF is designed with distributed systems in mind. Hence, all message types must CAF is designed with distributed systems in mind. Hence, all message types must
be serializable and need a platform-neutral, unique name that is configured at be serializable and need a platform-neutral, unique name (see
startup (see :ref:`add-custom-message-type`). Using a message type that is not :ref:`add-custom-message-type`). Using a message type that is not serializable
serializable causes a compiler error (see :ref:`unsafe-message-type`). CAF causes a compiler error (see :ref:`unsafe-message-type`). CAF serializes
serializes individual elements of a message by using the inspection API. This individual elements of a message by using the inspection API. This API allows
API allows users to provide code for serialization as well as string conversion users to provide code for serialization as well as string conversion, usually
with a single free function. The signature for a class ``my_class`` is always as with a single free function. The signature for a class ``my_class`` is always as
follows: follows:
...@@ -21,38 +21,41 @@ follows: ...@@ -21,38 +21,41 @@ follows:
The function ``inspect`` passes meta information and data fields to the The function ``inspect`` passes meta information and data fields to the
variadic call operator of the inspector. The following example illustrates an variadic call operator of the inspector. The following example illustrates an
implementation for ``inspect`` for a simple POD struct. implementation of ``inspect`` for a simple POD called ``foo``.
.. literalinclude:: /examples/custom_type/custom_types_1.cpp .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++ :language: C++
:lines: 23-33 :start-after: --(rst-foo-begin)--
:end-before: --(rst-foo-end)--
The inspector recursively inspects all data fields and has builtin support for The inspector recursively inspects all data fields and has builtin support for
(1) ``std::tuple``, (2) ``std::pair``, (3) C arrays, (4) any ``std::tuple``, ``std::pair``, arrays, as well as containers that provide the
container type with ``x.size()``, ``x.empty()``, member functions ``size``, ``empty``, ``begin`` and ``end``.
``x.begin()`` and ``x.end()``.
We consciously made the inspect API as generic as possible to allow for We consciously made the inspect API as generic as possible to allow for
extensibility. This allows users to use CAF's types in other contexts, to extensibility. This allows users to use CAF's types in other contexts, to
implement parsers, etc. implement parsers, etc.
.. note::
When converting a user-defined type to a string, CAF calls user-defined
``to_string`` functions and prefers those over ``inspect``.
Inspector Concept Inspector Concept
----------------- -----------------
The following concept class shows the requirements for inspectors. The The following concept class shows the requirements for inspectors. The
placeholder ``T`` represents any user-defined type. For example, placeholder ``T`` represents any user-defined type. Usually ``error`` or
``error`` when performing I/O operations or some integer type when ``error_code``.
implementing a hash function.
.. code-block:: C++ .. code-block:: C++
Inspector { Inspector {
using result_type = T; using result_type = T;
if (inspector only requires read access to the state of T) static constexpr bool reads_state = ...;
static constexpr bool reads_state = true;
else static constexpr bool writes_state = ...;
static constexpr bool writes_state = true;
template <class... Ts> template <class... Ts>
result_type operator()(Ts&&...); result_type operator()(Ts&&...);
...@@ -63,6 +66,13 @@ references. A loading ``Inspector`` must only accept mutable lvalue ...@@ -63,6 +66,13 @@ references. A loading ``Inspector`` must only accept mutable lvalue
references to data fields, but still allow for constant lvalue references and references to data fields, but still allow for constant lvalue references and
rvalue references to annotations. rvalue references to annotations.
Inspectors that only visit data fields (such as a serializer) sets
``reads_state`` to ``true`` and ``writes_state`` to ``false``. Inspectors that
override data fields (such as a deserializer) assign the inverse values.
These compile-time constants can be used in ``if constexpr`` statements or to
select different ``inspect`` overloads with ``enable_if``.
Annotations Annotations
----------- -----------
...@@ -74,27 +84,28 @@ inspector can query whether a type ``T`` is an annotation with ...@@ -74,27 +84,28 @@ inspector can query whether a type ``T`` is an annotation with
call operator of the inspector along with data fields. The following list shows call operator of the inspector along with data fields. The following list shows
all annotations supported by CAF: all annotations supported by CAF:
* ``type_name(n)``: Display type name as ``n`` in human-friendly output (position before data fields). - ``type_name(n)``: Display type name as ``n`` in human-friendly output
* ``hex_formatted()``: Format the following data field in hex format. (position before data fields).
* ``omittable()``: Omit the following data field in human-friendly output. - ``hex_formatted()``: Format the following data field in hex format.
* ``omittable_if_empty()``: Omit the following data field if it is empty in human-friendly output. - ``omittable()``: Omit the following data field in human-friendly output.
* ``omittable_if_none()``: Omit the following data field if it equals ``none`` in human-friendly output. - ``omittable_if_empty()``: Omit the following data field in human-friendly
* ``save_callback(f)``: Call ``f`` when serializing (position after data fields). output if it is empty.
* ``load_callback(f)``: Call ``f`` after deserializing all data fields (position after data fields). - ``omittable_if_none()``: Omit the following data field in human-friendly
output if it equals ``none``.
- ``save_callback(f)``: Call ``f`` if ``reads_state == true``. Pass this
callback after the data fields.
- ``load_callback(f)``: Call ``f`` ``writes_state == true``. Pass this callback
after the data fields.
Backwards and Third-party Compatibility Backwards and Third-party Compatibility
--------------------------------------- ---------------------------------------
CAF evaluates common free function other than ``inspect`` in order to CAF evaluates common free function other than ``inspect`` in order to simplify
simplify users to integrate CAF into existing code bases. users to integrate CAF into existing code bases.
Serializers and deserializers call user-defined ``serialize`` Serializers and deserializers call user-defined ``serialize`` functions. Both
functions. Both types support ``operator&`` as well as types support ``operator&`` as well as ``operator()`` for individual data
``operator()`` for individual data fields. A ``serialize`` fields. A ``serialize`` function has priority over ``inspect``.
function has priority over ``inspect``.
When converting a user-defined type to a string, CAF calls user-defined
``to_string`` functions and prefers those over ``inspect``.
.. _unsafe-message-type: .. _unsafe-message-type:
...@@ -102,32 +113,57 @@ Whitelisting Unsafe Message Types ...@@ -102,32 +113,57 @@ Whitelisting Unsafe Message Types
--------------------------------- ---------------------------------
Message types that are not serializable cause compile time errors when used in Message types that are not serializable cause compile time errors when used in
actor communication. When using CAF for concurrency only, this errors can be actor communication. For messages that never cross the network, this errors can
suppressed by whitelisting types with be suppressed by whitelisting types with ``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The
``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The macro is defined as follows. macro is defined as follows.
.. literalinclude:: /libcaf_core/caf/allowed_unsafe_message_type.hpp
:language: C++
:start-after: --(rst-macro-begin)--
:end-before: --(rst-macro-end)--
Keep in mind that ``unsafe`` means that your program runs into undefined
behavior (or segfaults) when you break your promise and try to serialize
messages that contain unsafe message types.
Splitting Save and Load Operations Saving and Loading with Getters and Setters
---------------------------------- -------------------------------------------
If loading and storing cannot be implemented in a single function, users can Many classes shield their member variables with getter and setter functions.
query whether the inspector is loading or storing. For example, consider the This can be addressed by declaring the ``inspect`` function as ``friend``, but
following class ``foo`` with getter and setter functions and no public only when not dealing with 3rd party library types. For example, consider the
access to its members. following class ``foo`` with getter and setter functions and no public access to
its members.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp .. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++ :language: C++
:lines: 20-49 :start-after: --(rst-foo-begin)--
:end-before: --(rst-foo-end)--
Since there is no access to the data fields ``a_`` and ``b_`` Since there is no access to the data fields ``a_`` and ``b_`` (and assuming no
(and assuming no changes to ``foo`` are possible), we need to split our changes to ``foo`` are possible), we can serialize or deserialize from/to local
implementation of ``inspect`` as shown below. variables and use a load callback to write back to the object when loading
state.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp .. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++ :language: C++
:lines: 76-103 :start-after: --(rst-inspect-begin)--
:end-before: --(rst-inspect-end)--
The purpose of the scope guard in the example above is to write the content of For more complicated cases, we can also split the inspect overload as follows:
the temporaries back to ``foo`` at scope exit automatically. Storing
the result of ``f(...)`` in a temporary first and then writing the .. code-block:: C++
changes to ``foo`` is not possible, because ``f(...)`` can
return ``void``. template <class Inspector>
typename std::enable_if<Inspector::reads_state,
typename Inspector::result_type>::type
inspect(Inspector& f, my_class& x) {
// ... serializing ...
}
template <class Inspector>
typename std::enable_if<Inspector::writes_state,
typename Inspector::result_type>::type
inspect(Inspector& f, my_class& x) {
// ... deserializing ...
}
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