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 IO using *
* a broker. Server and client exchange integers in a 'ping-pong protocol'. *
* *
* Minimal setup: *
* - ./build/bin/broker -s 4242 *
* - ./build/bin/broker -c localhost 4242 *
\
******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
// This example program showcases how to manually manage socket I/O using a
// broker. Server and client exchange integers in a "ping-pong protocol".
//
// Minimal setup:
// - ./build/bin/broker -s 4242
// - ./build/bin/broker -c localhost 4242
#include "caf/config.hpp"
......@@ -39,12 +34,14 @@ using namespace caf::io;
namespace {
// --(rst-attach-begin)--
// Utility function to print an exit message with custom name.
void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](const error& reason) {
cout << name << " exited: " << to_string(reason) << endl;
});
}
// --(rst-attach-end)--
enum class op : uint8_t {
ping,
......
// 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 <iostream>
#include <string>
......@@ -117,4 +114,4 @@ void caf_main(actor_system& sys) {
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
// if friend access for serialization is available
// Showcases how to add custom message types to CAF if friend access for
// serialization is available.
#include <utility>
#include <iostream>
......
// Showcases custom message types that cannot provide
// friend access to the inspect() function.
// Showcases custom message types that cannot provide friend access to the
// inspect() function.
#include <iostream>
#include <utility>
......
/******************************************************************************\
* This example is an implementation of the classical Dining Philosophers *
* exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/
// This example is an implementation of the classical Dining Philosophers
// exercise using only libcaf's event-based actor implementation.
#include <chrono>
#include <iostream>
......
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* for both the blocking and the event-based API. *
\******************************************************************************/
// Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
// This example is a very basic, non-interactive math service implemented for
// both the blocking and the event-based API.
#include <iostream>
......@@ -14,10 +10,13 @@ using namespace caf;
namespace {
// --(rst-actor-begin)--
using calculator_actor
= typed_actor<replies_to<add_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
behavior calculator_fun(event_based_actor* self);
void blocking_calculator_fun(blocking_actor* self);
......@@ -25,7 +24,9 @@ calculator_actor::behavior_type typed_calculator_fun();
class calculator;
class blocking_calculator;
class typed_calculator;
// --(rst-fwd-end)--
// --(rst-funs-begin)--
// function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) {
return {
......@@ -55,7 +56,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
[](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 calculator : public event_based_actor {
public:
......@@ -91,6 +94,7 @@ public:
return typed_calculator_fun();
}
};
// --(rst-classes-end)--
void tester(scoped_actor&) {
// end of recursion
......@@ -121,12 +125,14 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
}
void caf_main(actor_system& system) {
// --(rst-spawn-begin)--
auto a1 = system.spawn(blocking_calculator_fun);
auto a2 = system.spawn(calculator_fun);
auto a3 = system.spawn(typed_calculator_fun);
auto a4 = system.spawn<blocking_calculator>();
auto a5 = system.spawn<calculator>();
auto a6 = system.spawn<typed_calculator>();
// --(rst-spawn-end)--
scoped_actor self{system};
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);
......
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* 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)
// This example is a very basic, non-interactive math service implemented for
// both the blocking and the event-based API.
#include <cstdint>
#include <iostream>
......@@ -16,6 +10,7 @@ using std::cout;
using std::endl;
using namespace caf;
// --(rst-cell-begin)--
using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>;
......@@ -24,14 +19,19 @@ struct cell_state {
};
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; }};
return {
[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
}
behavior unchecked_cell(stateful_actor<cell_state>* self) {
return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; }};
return {
[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
}
// --(rst-cell-end)--
void caf_main(actor_system& system) {
// create one cell for each implementation
......
/******************************************************************************\
* This example illustrates how to do time-triggered loops in libcaf. *
\******************************************************************************/
// This example illustrates how to do time-triggered loops in CAF.
#include <algorithm>
#include <chrono>
......@@ -8,29 +6,26 @@
#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::endl;
using std::pair;
using namespace caf;
// ASCII art figures
// ASCII art figures.
constexpr const char* figures[] = {
"<(^.^<)",
"<(^.^)>",
"(>^.^)>",
};
// Bundes an index to an ASCII art figure plus its position on screen.
struct animation_step {
size_t figure_idx;
size_t offset;
};
// array of {figure, offset} pairs
// Array of {figure, offset} pairs.
constexpr animation_step animation_steps[] = {
{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},
......@@ -38,41 +33,48 @@ constexpr animation_step animation_steps[] = {
{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;
// "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) {
cout.width(animation_width);
// override last figure
// Override last figure.
cout << '\r';
// print offset
// Print left padding (offset).
std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' ');
// print figure
// Print figure.
cout << figures[animation.figure_idx];
// print padding
// Print right padding.
cout.fill(' ');
// make sure figure is printed
// Make sure figure is visible.
cout.flush();
}
// uses a message-based loop to iterate over all animation steps
void dancing_kirby(event_based_actor* self) {
// let's get it started
self->send(self, update_atom_v, size_t{0});
self->become([=](update_atom, size_t step) {
if (step == sizeof(animation_step)) {
// we've printed all animation steps (done)
// --(rst-dancing-kirby-begin)--
// Uses a message-based loop to iterate over all animation steps.
behavior dancing_kirby(event_based_actor* self) {
// Let's get started.
auto i = std::begin(animation_steps);
auto e = std::end(animation_steps);
self->send(self, update_atom_v);
return {
[=](update_atom) mutable {
// We're done when reaching the past-the-end position.
if (i == e) {
cout << endl;
self->quit();
return;
}
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
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) {
system.spawn(dancing_kirby);
......
#include <cstdint>
#include <iostream>
#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 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>>;
void actor_a(event_based_actor* self, const calc& worker) {
......@@ -32,6 +28,7 @@ calc::behavior_type actor_c() {
[](add_atom, int32_t x, int32_t y) { return x + y; },
};
}
// --(rst-delegate-end)--
void caf_main(actor_system& system) {
system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c)));
......
/******************************************************************************\
* A very basic, interactive divider. *
\******************************************************************************/
// Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 17-47 (Error)
// A basic, interactive divider.
#include <iostream>
......@@ -14,10 +9,21 @@ using std::endl;
using std::flush;
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 {
division_by_zero = 1,
};
CAF_ERROR_CODE_ENUM(math_error)
std::string to_string(math_error x) {
switch (x) {
case math_error::division_by_zero:
......@@ -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>>;
divider::behavior_type divider_impl() {
......@@ -46,6 +44,7 @@ divider::behavior_type divider_impl() {
},
};
}
// --(rst-divider-end)--
void caf_main(actor_system& system) {
double x;
......@@ -54,6 +53,7 @@ void caf_main(actor_system& system) {
std::cin >> x;
cout << "y: " << flush;
std::cin >> y;
// --(rst-request-begin)--
auto div = system.spawn(divider_impl);
scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
......@@ -63,6 +63,7 @@ void caf_main(actor_system& system) {
aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< to_string(err) << endl;
});
// --(rst-request-end)--
}
CAF_MAIN(id_block::divider)
#include "caf/all.hpp"
#include <cassert>
#include <cstdint>
#include <iostream>
#include "caf/all.hpp"
enum class ec : uint8_t {
push_to_full = 1,
pop_from_empty,
......
/******************************************************************************\
* 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)
// Illustrates how to use response promises.
#include <iostream>
......@@ -15,17 +9,16 @@ using std::endl;
using namespace caf;
// --(rst-promises-begin)--
using adder
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
// function-based, statically typed, event-based API
adder::behavior_type worker() {
return {
[](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) {
auto w = self->spawn(worker);
return {
......@@ -38,6 +31,7 @@ adder::behavior_type calculator_master(adder::pointer self) {
},
};
}
// --(rst-promises-end)--
void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master));
......
/******************************************************************************\
* 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)
// Illustrates semantics of request().{then|await|receive}.
#include <chrono>
#include <cstdint>
......@@ -16,8 +10,10 @@
using std::endl;
using std::vector;
using std::chrono::seconds;
using namespace caf;
// --(rst-cell-begin)--
using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>;
......@@ -59,11 +55,14 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
});
}
// --(rst-cell-end)--
void caf_main(actor_system& system) {
vector<cell> cells;
// --(rst-spawn-begin)--
for (auto i = 0; i < 5; ++i)
cells.emplace_back(system.spawn(cell_impl, i * i));
// --(rst-spawn-end)--
scoped_actor self{system};
aout(self) << "waiting_testee" << endl;
auto x1 = self->spawn(waiting_testee, cells);
......
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* using typed actors. *
\******************************************************************************/
// This example is a very basic, non-interactive math service implemented using
// typed actors.
#include "caf/all.hpp"
#include <cassert>
......
......@@ -9,8 +9,6 @@
// Run client at the same host:
// - ./build/bin/distributed_math_actor -c -p 4242
// Manual refs: 206-218 (ConfiguringActorSystems)
#include <array>
#include <cassert>
#include <functional>
......@@ -199,6 +197,7 @@ optional<int> toint(const string& str) {
return none;
}
// --(rst-config-begin)--
class config : public actor_system_config {
public:
uint16_t port = 0;
......@@ -212,6 +211,7 @@ public:
.add(server_mode, "server-mode,s", "enable server mode");
}
};
// --(rst-config-end)--
void client_repl(actor_system& system, const config& cfg) {
// keeps track of requests and tries to reconnect on server failures
......
/******************************************************************************\
* This example program represents a minimal terminal chat program *
* based on group communication. *
* *
* Setup for a minimal chat between "alice" and "bob": *
* - ./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 bob *
\******************************************************************************/
// This example program represents a minimal terminal chat program based on
// group communication.
//
// Setup for a minimal chat between "alice" and "bob":
// - ./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 bob
#include <cstdlib>
#include <iostream>
......
/******************************************************************************\
* This example program represents a minimal IRC-like group *
* communication server. *
* *
* Setup for a minimal chat between "alice" and "bob": *
* - ./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 bob *
\ ******************************************************************************/
// This example program represents a minimal IRC-like group communication
// server.
//
// Setup for a minimal chat between "alice" and "bob":
// - ./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 bob
#include <string>
#include <cstdlib>
......
// This program illustrates how to spawn a simple calculator
// across the network.
// This program illustrates how to spawn a simple calculator across the network.
//
// Run server at port 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 <vector>
......
// Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong
#include "caf/test/dsl.hpp"
......@@ -9,6 +7,7 @@
using namespace caf;
// --(rst-ping-pong-begin)--
namespace {
behavior ping(event_based_actor* self, actor pong_actor, int n) {
......@@ -58,3 +57,4 @@ CAF_TEST(three pings) {
}
CAF_TEST_FIXTURE_SCOPE_END()
// --(rst-ping-pong-end)--
......@@ -16,9 +16,6 @@
* 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
#include <type_traits>
......@@ -45,9 +42,10 @@ struct is_allowed_unsafe_message_type<const T&>
} // namespace caf
// --(rst-macro-begin)--
#define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \
namespace caf { \
template <> \
struct allowed_unsafe_message_type<type_name> : std::true_type {}; \
}
// --(rst-macro-end)--
......@@ -16,10 +16,6 @@
* 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
#include <string>
......@@ -28,6 +24,7 @@
namespace caf {
// --(rst-exit-reason-begin)--
/// This error category represents fail conditions for actors.
enum class exit_reason : uint8_t {
/// Indicates that an actor finished execution without error.
......@@ -49,6 +46,7 @@ enum class exit_reason : uint8_t {
/// Indicates that an actor was killed because it became unreachable.
unreachable
};
// --(rst-exit-reason-end)--
/// Returns a string representation of given exit reason.
std::string to_string(exit_reason);
......
......@@ -16,10 +16,6 @@
* 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
#include "caf/error.hpp"
......@@ -27,6 +23,7 @@
namespace caf {
// --(rst-sec-begin)--
/// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules.
enum class sec : uint8_t {
......@@ -140,6 +137,7 @@ enum class sec : uint8_t {
/// Disconnected from a BASP node after reaching the connection timeout.
connection_timeout,
};
// --(rst-sec-end)--
/// @relates sec
std::string to_string(sec);
......
......@@ -3,19 +3,22 @@
Actors
======
Actors in CAF are a lightweight abstraction for units of computations. They
are active objects in the sense that they own their state and do not allow
Actors in CAF represent a lightweight abstraction for units of computations.
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
messages to it.
CAF provides several actor implementations, each covering a particular use
case. The available implementations differ in three characteristics: (1)
dynamically or statically typed, (2) class-based or function-based, and (3)
using asynchronous event handlers or blocking receives. 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``.
case. The available implementations differ in three characteristics:
#. dynamically or statically typed
#. class-based or function-based
#. using asynchronous event handlers or blocking receives
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
Akka. They (usually) enable faster prototyping but require extensive unit
......@@ -28,7 +31,7 @@ visible across multiple translation units.
Actors that utilize the blocking receive API always require an exclusive thread
of execution. Event-based actors, on the other hand, are usually scheduled
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
detached actor lives in its own thread of execution.
......@@ -38,10 +41,10 @@ Environment / Actor Systems
---------------------------
All actors live in an ``actor_system`` representing an actor environment
including :ref:`scheduler`, :ref:`registry`, and optional components such as a
:ref:`middleman`. A single process can have multiple ``actor_system`` instances,
but this is usually not recommended (a use case for multiple systems is to
strictly separate two or more sets of actors by running them in different
including :ref:`scheduler`, :ref:`registry`, and optional components (modules)
such as a :ref:`middleman`. A single process can have multiple ``actor_system``
instances, but this is usually not recommended (a use case for multiple systems
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
:ref:`system-config`. A distributed CAF application consists of two or more
connected actor systems. We also refer to interconnected ``actor_system``
......@@ -71,19 +74,15 @@ user-defined actors are ``event_based_actor`` or
inherited from ``monitorable_actor`` and ``abstract_actor``.
+-------------------------------------+--------------------------------------------------------+
| **Types** | |
| **Types** |
+-------------------------------------+--------------------------------------------------------+
| ``mailbox_type`` | A concurrent, many-writers-single-reader queue type. |
+-------------------------------------+--------------------------------------------------------+
| | |
+-------------------------------------+--------------------------------------------------------+
| **Constructors** | |
| **Constructors** |
+-------------------------------------+--------------------------------------------------------+
| ``(actor_config&)`` | Constructs the actor using a config. |
+-------------------------------------+--------------------------------------------------------+
| | |
+-------------------------------------+--------------------------------------------------------+
| **Observers** | |
| **Observers** |
+-------------------------------------+--------------------------------------------------------+
| ``actor_addr address()`` | Returns the address of this actor. |
+-------------------------------------+--------------------------------------------------------+
......@@ -93,17 +92,15 @@ inherited from ``monitorable_actor`` and ``abstract_actor``.
+-------------------------------------+--------------------------------------------------------+
| ``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. |
+-------------------------------------+--------------------------------------------------------+
| | |
+-------------------------------------+--------------------------------------------------------+
| **Actor Management** | |
| **Actor Management** |
+-------------------------------------+--------------------------------------------------------+
| ``link_to(other)`` | Links to ``other`` (see :ref:`link`). |
+-------------------------------------+--------------------------------------------------------+
......@@ -117,16 +114,13 @@ inherited from ``monitorable_actor`` and ``abstract_actor``.
+-------------------------------------+--------------------------------------------------------+
| ``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 response(xs...)`` | Convenience function for creating fulfilled promises. |
+-------------------------------------+--------------------------------------------------------+
Class ``scheduled_actor``
~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -135,7 +129,7 @@ statically and dynamically typed event-based actors as well as brokers
:ref:`broker`.
+-------------------------------+--------------------------------------------------------------------------+
| **Types** | |
| **Types** |
+-------------------------------+--------------------------------------------------------------------------+
| ``pointer`` | ``scheduled_actor*`` |
+-------------------------------+--------------------------------------------------------------------------+
......@@ -149,23 +143,17 @@ statically and dynamically typed event-based actors as well as brokers
+-------------------------------+--------------------------------------------------------------------------+
| ``exit_handler`` | ``function<void (pointer, exit_msg&)>`` |
+-------------------------------+--------------------------------------------------------------------------+
| | |
+-------------------------------+--------------------------------------------------------------------------+
| **Constructors** | |
| **Constructors** |
+-------------------------------+--------------------------------------------------------------------------+
| ``(actor_config&)`` | Constructs the actor using a config. |
+-------------------------------+--------------------------------------------------------------------------+
| | |
+-------------------------------+--------------------------------------------------------------------------+
| **Termination** | |
| **Termination** |
+-------------------------------+--------------------------------------------------------------------------+
| ``quit()`` | Stops this actor with normal exit reason. |
+-------------------------------+--------------------------------------------------------------------------+
| ``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`). |
+-------------------------------+--------------------------------------------------------------------------+
......@@ -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``
sends its exit messages as part of its destruction.
+----------------------------------+---------------------------------------------------+
| **Constructors** | |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| **Constructors** |
+-----------------------------------+---------------------------------------------------+
| ``(actor_config&)`` | Constructs the actor using a config. |
+----------------------------------+---------------------------------------------------+
| | |
+----------------------------------+---------------------------------------------------+
| **Customization Points** | |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| **Customization Points** |
+-----------------------------------+---------------------------------------------------+
| ``void act()`` | Implements the behavior of the actor. |
+----------------------------------+---------------------------------------------------+
| | |
+----------------------------------+---------------------------------------------------+
| **Termination** | |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| **Termination** |
+-----------------------------------+---------------------------------------------------+
| ``const error& fail_state()`` | Returns the current exit reason. |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| ``fail_state(error x)`` | Sets the current exit reason. |
+----------------------------------+---------------------------------------------------+
| | |
+----------------------------------+---------------------------------------------------+
| **Actor Management** | |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| **Actor Management** |
+-----------------------------------+---------------------------------------------------+
| ``wait_for(Ts... xs)`` | Blocks until all actors ``xs...`` are done. |
+----------------------------------+---------------------------------------------------+
| ``await_all_other_actors_done()``| Blocks until all other actors are done. |
+----------------------------------+---------------------------------------------------+
| | |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| ``await_all_other_actors_done()`` | Blocks until all other actors are done. |
+-----------------------------------+---------------------------------------------------+
| **Message Handling** | |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| ``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_. |
+----------------------------------+---------------------------------------------------+
+-----------------------------------+---------------------------------------------------+
| ``receive_for(T& begin, T end)`` | See :ref:`receive-loop`. |
+-----------------------------------+---------------------------------------------------+
| ``receive_while(F stmt)`` | See :ref:`receive-loop`. |
+-----------------------------------+---------------------------------------------------+
| ``do_receive(Ts... xs)`` | See :ref:`receive-loop`. |
+-----------------------------------+---------------------------------------------------+
.. _interface:
......@@ -250,7 +230,8 @@ messaging interface for a simple calculator.
.. literalinclude:: /examples/message_passing/calculator.cpp
: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``,
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.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 21-26
:start-after: --(rst-fwd-begin)--
:end-before: --(rst-fwd-end)--
Spawning an actor for each implementation is illustrated below.
.. literalinclude:: /examples/message_passing/calculator.cpp
: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
class or used as additional function arguments, respectively. In the example
above, none of the three functions takes any argument other than the implicit
but optional ``self`` pointer.
Additional arguments to ``spawn`` are passed to the constructor of a class or
used as additional function arguments, respectively. In the example above, none
of the three functions takes any argument other than the implicit (optional)
``self`` pointer.
.. _function-based:
......@@ -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
``blocking_actor*``. The proper pointer type for any
``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
is done once it returns from that function.
......@@ -344,7 +327,8 @@ dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 28-56
:start-after: --(rst-funs-begin)--
:end-before: --(rst-funs-end)--
.. _class-based:
......@@ -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
simple management of state via member variables. However, composing states via
inheritance can get quite tedious. For dynamically typed actors, composing
states is particularly hard, because the compiler cannot provide much help. For
statically typed actors, CAF also provides an API for composable
behaviors composable-behavior_ that works well with inheritance. The
following three examples implement the forward declarations shown in
spawn_.
states is particularly hard, because the compiler cannot provide much help.
The following three classes implement the prototypes shown in :ref:`spawn` and
again feature one blocking actor and two event-based actors (statically and
dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 58-92
:start-after: --(rst-classes-begin)--
:end-before: --(rst-classes-end)--
.. _stateful-actor:
......@@ -390,77 +375,11 @@ typing as well as with dynamic typing.
.. literalinclude:: /examples/message_passing/cell.cpp
: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
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
(see :ref:`function-based`).
.. _attach:
......@@ -474,7 +393,8 @@ printing a custom string on exit.
.. literalinclude:: /examples/broker/simple_broker.cpp
: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
run on the local machine.
......@@ -608,9 +528,8 @@ more efficient code.
).until([&] { return received >= 10; });
The examples above illustrate the correct usage of the three loops
``receive_for``, ``receive_while`` and
``do_receive(...).until``. It is possible to nest receives and receive
loops.
``receive_for``, ``receive_while`` and ``do_receive(...).until``. It is possible
to nest receives and receive loops.
.. code-block:: C++
......@@ -631,11 +550,10 @@ loops.
Scoped Actors
~~~~~~~~~~~~~
The class ``scoped_actor`` offers a simple way of communicating with
CAF actors from non-actor contexts. It overloads ``operator->`` to
return a ``blocking_actor*``. Hence, it behaves like the implicit
``self`` pointer in functor-based actors, only that it ceases to exist
at scope end.
The class ``scoped_actor`` offers a simple way of communicating with CAF actors
from non-actor contexts. It overloads ``operator->`` to return a
``blocking_actor*``. Hence, it behaves like the implicit ``self`` pointer in
functor-based actors, only that it ceases to exist at scope end.
.. code-block:: C++
......@@ -647,4 +565,3 @@ at scope end.
// self will be destroyed automatically here; any
// actor monitoring it will receive down messages etc.
}
......@@ -3,11 +3,11 @@
Configuring Actor Applications
==============================
CAF configures applications at startup using an
``actor_system_config`` or a user-defined subclass of that type. The
config objects allow users to add custom types, to load modules, and to
fine-tune the behavior of loaded modules with command line options or
configuration files system-config-options_.
CAF configures applications at startup using an ``actor_system_config`` or a
user-defined subclass of that type. The config objects allow users to add custom
types, to load modules (optional components), and to fine-tune the behavior of
loaded modules with command line options or configuration files (see
:ref:`system-config-options`).
The following code example is a minimal CAF application with a :ref:`middleman`
but without any custom configuration options.
......@@ -30,34 +30,58 @@ The compiler expands this example code to the following.
return exec_main<io::middleman>(caf_main, argc, argv);
}
The function ``exec_main`` creates a config object, loads all modules
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.
The function ``exec_main`` performs several steps:
#. 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``.
When implementing the steps performed by ``CAF_MAIN`` by hand, the ``main``
function would resemble the following (pseudo) code:
.. code-block:: C++
int main(int argc, char** argv) {
// Create the config.
actor_system_config cfg;
// read CLI options
cfg.parse(argc, argv);
// return immediately if a help text was printed
// 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 0;
// load modules
cfg.load<io::middleman>();
// create actor system and call caf_main
actor_system system{cfg};
caf_main(system);
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;
}
However, setting up config objects by hand is usually not necessary. CAF
automatically selects user-defined subclasses of
``actor_system_config`` if ``caf_main`` takes a second
parameter by reference, as shown in the minimal example below.
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++
CAF_BEGIN_TYPE_ID_BLOCK(my, first_custom_type_id)
// ...
CAF_END_TYPE_ID_BLOCK(my)
class my_config : public actor_system_config {
public:
my_config() {
......@@ -69,10 +93,12 @@ parameter by reference, as shown in the minimal example below.
// ...
}
CAF_MAIN()
CAF_MAIN(id_block::my, io::middleman)
.. note::
Users can perform additional initialization, add custom program options, etc.
simply by implementing a default constructor.
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:
......@@ -110,15 +136,16 @@ object *before* initializing an actor system with it.
Command Line Options and INI Configuration Files
------------------------------------------------
CAF organizes program options in categories and parses CLI arguments as well
as INI files. CLI arguments override values in the INI file which override
CAF organizes program options in categories and parses CLI arguments as well as
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
implementing a subtype of ``actor_system_config``. The example below
adds three options to the ``global`` category.
.. literalinclude:: /examples/remoting/distributed_calculator.cpp
:language: C++
:lines: 206-218
:start-after: --(rst-config-begin)--
:end-before: --(rst-config-end)--
We create a new ``global`` category in ``custom_options_``.
Each following call to ``add`` then appends individual options to the
......@@ -156,23 +183,25 @@ command line.
INI files are organized in categories. No value is allowed outside of a category
(no implicit ``global`` category). The parses uses the following syntax:
+------------------------+-----------------------------+
| ``key=true`` | is a boolean |
+------------------------+-----------------------------+
| ``key=1`` | is an integer |
+------------------------+-----------------------------+
| ``key=1.0`` | is an floating point number |
+------------------------+-----------------------------+
| ``key=1ms`` | is an timespan |
+------------------------+-----------------------------+
| ``key='foo'`` | is an atom |
+------------------------+-----------------------------+
| ``key="foo"`` | is a string |
+------------------------+-----------------------------+
| ``key=[0, 1, ...]`` | is as a list |
+------------------------+-----------------------------+
| ``key={a=1, b=2, ...}``| is a dictionary (map) |
+------------------------+-----------------------------+
+-------------------------+-----------------------+
| Syntax | Type |
+=========================+=======================+
| ``key=true`` | Boolean |
+-------------------------+-----------------------+
| ``key=1`` | Integer |
+-------------------------+-----------------------+
| ``key=1.0`` | Floating point number |
+-------------------------+-----------------------+
| ``key=1ms`` | Timespan |
+-------------------------+-----------------------+
| ``key='foo'`` | Atom |
+-------------------------+-----------------------+
| ``key="foo"`` | String |
+-------------------------+-----------------------+
| ``key=[0, 1, ...]`` | List |
+-------------------------+-----------------------+
| ``key={a=1, b=2, ...}`` | Dictionary (map) |
+-------------------------+-----------------------+
The following example INI file lists all standard options in CAF and their
default value. Note that some options such as ``scheduler.max-threads``
......@@ -187,42 +216,66 @@ Adding Custom Message Types
---------------------------
CAF requires serialization support for all of its message types (see
:ref:`type-inspection`). However, CAF also needs a mapping of unique type names
to user-defined types at runtime. This is required to deserialize arbitrary
messages from the network.
As an introductory example, we (again) use the following POD type
``foo``.
:ref:`type-inspection`). In addition, CAF also needs a mapping of unique type
names to user-defined types at runtime. This is required to deserialize
arbitrary messages from the network.
The function ``actor_system_config::add_message_type`` adds runtime-type
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
: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
:language: C++
:lines: 30-34
The second argument to ``CAF_ADD_TYPE_ID`` (the type) must appear in extra
parentheses. This unusual syntax is an artifact of argument handling in
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
of serializable types by using a custom config class.
The first argument to all macros is the name of the type ID block. The macro
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
:language: C++
:lines: 75-78,81-84
.. code-block:: C++
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
improve the string representation. Error types can be added by implementing a
render function and passing it to ``add_error_category``, as shown in
:ref:`custom-error`.
At the point of calling ``CAF_MAIN`` or ``add_message_types``, the compiler
must have the type declaration plus all ``inspect`` overloads available for
each type in the type ID block.
.. _add-custom-actor-type:
Adding Custom Actor Types :sup:`experimental`
----------------------------------------------
---------------------------------------------
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
......@@ -231,33 +284,29 @@ simple ``calculator`` actor.
.. literalinclude:: /examples/remoting/remote_spawn.cpp
: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
``add_actor_type<T>``. Note that adding an actor type in this way
implicitly calls ``add_message_type<T>`` for typed actors
add-custom-message-type_. This makes our ``calculator`` actor type
serializable and also enables remote nodes to spawn calculators anywhere in the
distributed actor system (assuming all nodes use the same config).
``add_actor_type``. After calling this in our config, we can spawn the
``calculator`` anywhere in the distributed actor system (assuming all nodes use
the same config). Note that the handle type still requires a type ID (see
:ref:`add-custom-message-type`).
.. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 98-109
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>``.
After adding the actor type to the config, we can spawn our ``calculator`` by
name. Unlike the regular ``spawn`` overloads, this version requires wrapping the
constructor arguments into a ``message`` and the function might fail and thus
returns an ``expected``:
.. code-block:: C++
auto x = system.spawn<calculator>("calculator", make_message());
if (! x) {
std::cerr << "*** unable to spawn calculator: "
<< system.render(x.error()) << std::endl;
return;
if (auto x = system.spawn<calculator>("calculator", make_message())) {
// ... do something with *x ...
} else {
std::cerr << "*** unable to spawn a calculator: " << to_string(x.error())
<< std::endl;
// ...
}
calculator c = std::move(*x);
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
......@@ -268,25 +317,29 @@ one string is created with:
auto worker = system.spawn<actor>("foo", make_message("bar"));
Because constructor (or function) arguments for spawning the actor are stored
in a ``message``, only actors with appropriate input types are allowed.
For example, pointer types are illegal. Hence users need to replace C-strings
with ``std::string``.
Because constructor (or function) arguments for spawning the actor are stored in
a ``message``, only actors with appropriate input types are allowed. Pointer
types, for example, are illegal in messages.
.. _log-output:
Log Output
----------
Logging is disabled in CAF per default. It can be enabled by setting the
``--with-log-level=`` option of the ``configure`` script to one
of ``error``, ``warning``, ``info``, ``debug``,
or ``trace`` (from least output to most). Alternatively, setting the
CMake variable ``CAF_LOG_LEVEL`` to one of these values has the same
effect.
CAF comes with a logger integrated into the actor system. By default, CAF itself
won't emit any log messages. Developers can set the verbosity of CAF itself at
build time by setting the CMake option ``CAF_LOG_LEVEL`` manually or by passing
``--with-log-level=...`` to the ``configure`` script. The available verbosity
levels are (from least to most output):
- ``error``
- ``warning``
- ``info``
- ``debug``
- ``trace``
All logger-related configuration options listed here and in
system-config-options_ are silently ignored if logging is disabled.
The logging infrastructure is always available to users, regardless of the
verbosity level of CAF itself.
.. _log-output-file-name:
......@@ -296,25 +349,25 @@ File Name
The output file is generated from the template configured by
``logger-file-name``. This template supports the following variables.
+----------------+--------------------------------+
| **Variable** | **Output** |
+----------------+--------------------------------+
+-----------------+--------------------------------+
| Variable | Output |
+=================+================================+
| ``[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. |
+----------------+--------------------------------+
+-----------------+--------------------------------+
.. _log-output-console:
Console
~~~~~~~
Console output is disabled per default. Setting ``logger-console`` to
either ``"uncolored"`` or ``"colored"`` prints log events to
``std::clog``. Using the ``"colored"`` option will print the
log events in different colors depending on the severity level.
Console output is disabled per default. Setting ``logger-console`` to either
``uncolored`` or ``colored`` prints log events to ``std::clog``. Using the
``colored`` option will print the log events in different colors depending on
the severity level.
.. _log-output-format-strings:
......@@ -326,35 +379,35 @@ events via ``logger-file-format`` and
``logger-console-format``. Note that format modifiers are not supported
at the moment. The recognized field identifiers are:
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| **Character**| **Output** |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``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``. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``d`` | The date in ISO 8601 format, i.e., ``"YYYY-MM-DDThh:mm:ss"``. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``F`` | The file name. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``L`` | The line number. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``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``. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``n`` | A newline. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``p`` | The priority (severity level). |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``r`` | Elapsed time since starting the application in milliseconds. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``t`` | ID of the current thread. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``a`` | ID of the current actor (or ``actor0`` when not logging inside an actor). |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
| ``%`` | A single percent sign. |
+--------------+-----------------------------------------------------------------------------------------------------------------------------+
+---------+-----------------------------------------------------------------------------------------------------------------------+
| Pattern | Output |
+=========+=======================================================================================================================+
| ``%c`` | The category/component. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%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"``. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%F`` | The file name. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%L`` | The line number. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%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``. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%n`` | A newline. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%p`` | The priority (severity level). |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%r`` | Elapsed time since starting the application in milliseconds. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%t`` | ID of the current thread. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%a`` | ID of the current actor (or ``actor0`` when not logging inside an actor). |
+---------+-----------------------------------------------------------------------------------------------------------------------+
| ``%%`` | A single percent sign. |
+---------+-----------------------------------------------------------------------------------------------------------------------+
.. _log-output-filtering:
......
......@@ -12,55 +12,63 @@ include a message to provide additional context information.
Class Interface
---------------
+-----------------------------------------+--------------------------------------------------------------------+
| **Constructors** | |
+-----------------------------------------+--------------------------------------------------------------------+
+------------------------------------------+--------------------------------------------------------------------+
| **Constructors** |
+------------------------------------------+--------------------------------------------------------------------+
| ``(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, message z)``| Construct error with code ``x``, category ``y``, and context ``z`` |
+-----------------------------------------+--------------------------------------------------------------------+
| | |
+-----------------------------------------+--------------------------------------------------------------------+
| **Observers** | |
+-----------------------------------------+--------------------------------------------------------------------+
+------------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y, message z)`` | Construct error with code ``x``, category ``y``, and context ``z`` |
+------------------------------------------+--------------------------------------------------------------------+
| **Observers** |
+------------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code |
+-----------------------------------------+--------------------------------------------------------------------+
+------------------------------------------+--------------------------------------------------------------------+
| ``atom_value category()`` | Returns the error category |
+-----------------------------------------+--------------------------------------------------------------------+
+------------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+
+------------------------------------------+--------------------------------------------------------------------+
| ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+
+------------------------------------------+--------------------------------------------------------------------+
.. _custom-error:
Add Custom Error Categories
---------------------------
Adding custom error categories requires three steps: (1) declare an enum class
of type ``uint8_t`` with the first value starting at 1, (2) specialize
``error_category`` to give your type a custom ID (value 0-99 are
reserved by CAF), and (3) add your error category to the actor system config.
The following example adds custom error codes for math errors.
Adding custom error categories requires two steps: (1) declare an enum class of
type ``uint8_t`` with the first value starting at 1, (2) enable conversions from
the error code enum to ``error`` by using the macro ``CAF_ERROR_CODE_ENUM``. The
following example illustrates these two steps with a ``math_error`` enum to
signal divisions by zero:
.. literalinclude:: /examples/message_passing/divider.cpp
: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:
System Error Codes
------------------
System Error Codes (SECs) use the error category ``"system"``. They
represent errors in the actor system or one of its modules and are defined as
follows.
System Error Codes (SECs) represent errors in the actor system or one of its
modules and are defined as follows:
.. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++
:lines: 32-117
:start-after: --(rst-sec-begin)--
:end-before: --(rst-sec-end)--
.. _exit-reason:
......@@ -77,5 +85,5 @@ in the same way. The latter terminates an actor unconditionally when used in
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp
: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.
std::string id = "foo";
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
std::cerr << "*** cannot load group: "
<< system.render(expected_grp.error()) << std::endl;
std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
<< std::endl;
return;
}
auto grp = std::move(*expected_grp);
......
......@@ -45,10 +45,10 @@ Dynamically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~~
A dynamically typed actor accepts any kind of message and dispatches on its
content dynamically at the receiver. This is the "traditional" messaging
style found in implementations like Erlang or Akka. The upside of this approach
is (usually) faster prototyping and less code. This comes at the cost of
requiring excessive testing.
content dynamically at the receiver. This is the traditional messaging style
found in implementations like Erlang or Akka. The upside of this approach is
(usually) faster prototyping and less code. This comes at the cost of requiring
excessive testing.
Statically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~
......
......@@ -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
results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together
to create a single result. The function is called with the result object
(initialed using ``init``) and the current result messages from a
worker.
The join function is responsible for ``glueing`` all result messages together to
create a single result. The function is called with the result object (initialed
using ``init``) and the current result messages from a worker.
The first argument of a split function is a mapping from actors (workers) to
tasks (messages). The second argument is the input message. The default split
......
......@@ -19,7 +19,7 @@ change its behavior when not receiving message after a certain amount of time.
message_handler x1{
[](int i) { /*...*/ },
[](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
......@@ -31,7 +31,7 @@ other message is not matched and gets forwarded to the default handler (see
message_handler x2{
[](double db) { /*...*/ },
[](double db) { /* - unreachable - */ }
[](double db) { /* - unreachable - */ },
};
Our second example illustrates an important characteristic of the matching
......@@ -55,37 +55,54 @@ The handler ``x4`` will consume messages with a single
``double`` using the first callback in ``x2``, essentially
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:
Atoms
-----
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
a mathematical service for integers. It receives two integers, performs a
simple way to annotate messages with meta data. Imagine an actor that provides a
mathematical service for integers. It receives two integers, performs a
user-defined operation and returns the result. Without additional context, the
actor cannot decide whether it should multiply or add the integers. Thus, the
operation must be encoded into the message. The Erlang programming language
introduced an approach to use non-numerical constants, so-called
*atoms*, which have an unambiguous, special-purpose type and do not have
the runtime overhead of string constants.
actor cannot decide whether it should multiply or add the integers, for example.
Thus, the operation must be encoded into the message. The Erlang programming
language introduced an approach to use non-numerical constants, so-called
*atoms*, which have an unambiguous, special-purpose type and do not have the
runtime overhead of string constants.
Atoms 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
characters and prohibits special characters. Legal characters are
``_0-9A-Za-z`` and the whitespace character. Atoms are created using
the ``constexpr`` function ``atom``, as the following example
illustrates.
characters and prohibits special characters. Legal characters are ``_0-9A-Za-z``
and the whitespace character. Atoms are created using the ``constexpr`` function
``atom``, as the following example illustrates.
.. code-block:: C++
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
**Warning**: The compiler cannot enforce the restrictions at compile time,
except for a length check. The assertion ``atom("!?") != atom("?!")``
is not true, because each invalid character translates to a whitespace
character.
.. warning::
The compiler cannot enforce the restrictions at compile time, except for a
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
uniquely typed and thus cannot be used in the signature of a callback. To
......
......@@ -4,12 +4,11 @@ Message Passing
===============
Message passing in CAF is always asynchronous. Further, CAF neither guarantees
message delivery nor message ordering in a distributed setting. CAF uses TCP
per default, but also enables nodes to send messages to other nodes without
having a direct connection. In this case, messages are forwarded by
intermediate nodes and can get lost if one of the forwarding nodes fails.
Likewise, forwarding paths can change dynamically and thus cause messages to
arrive out of order.
message delivery nor message ordering in a distributed setting. CAF uses TCP per
default, but also enables nodes to send messages to other nodes without having a
direct connection. In this case, messages are forwarded by intermediate nodes
and can get lost if one of the forwarding nodes fails. Likewise, forwarding
paths can change dynamically and thus cause messages to arrive out of order.
The messaging layer of CAF has three primitives for sending messages: ``send``,
``request``, and ``delegate``. The former simply enqueues a message to the
......@@ -22,8 +21,8 @@ Structure of Mailbox Elements
-----------------------------
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
and processing paths.
the message into a ``mailbox_element`` (shown below) to add meta data and
processing paths.
.. _mailbox_element:
......@@ -69,11 +68,6 @@ Message types in CAF must meet the following requirements:
2. Default 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
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
......@@ -84,8 +78,8 @@ it. Requirement 3 allows CAF to implement Copy on Write (see
Default and System Message Handlers
-----------------------------------
CAF has three system-level message types (``down_msg``, ``exit_msg``, and
``error``) that all actor should handle regardless of there current state.
CAF has a couple of system-level message types such as ``down_msg`` and
``exit_msg`` that all actor should handle regardless of there current state.
Consequently, event-based actors handle such messages in special-purpose message
handlers. Additionally, event-based actors have a fallback handler for unmatched
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
``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
fail collectively. Actors can override the default handler via
``set_exit_handler(f)``, where ``f`` is a function object with signature
``void (exit_message&)`` or ``void (scheduled_actor*, exit_message&)``.
``set_exit_handler(f)``, where ``f`` is a function object with signature ``void
(exit_message&)`` or ``void (scheduled_actor*, exit_message&)``.
.. _error-message:
......@@ -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
signature ``void (error&)`` or ``void (scheduled_actor*, error&)``.
Additionally, ``request`` accepts an error handler as second argument to handle
errors for a particular request. The default handler is used as fallback if
``request`` is used without error handler.
errors for a particular request (see :ref:`error-response`). The default handler
is used as fallback if ``request`` is used without error handler.
.. _default-handler:
......@@ -170,53 +164,48 @@ and to provide a convenient API for this style of communication.
Sending Requests and Handling Responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Actors send request messages by calling ``request(receiver, timeout,
content...)``. This function returns an intermediate object that allows an actor
to set a one-shot handler for the response message. Event-based actors can use
either ``request(...).then`` or ``request(...).await``. The former multiplexes
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.
Actors send request messages by calling
``request(receiver, timeout, content...)``. This function returns an
intermediate object that allows an actor to set a one-shot handler for the
response message.
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 20-37
Event-based actors can use either ``request(...).then`` or
``request(...).await``. The former multiplexes 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.
The first part of the example illustrates how event-based actors can use either
``then`` or ``await``.
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 known to run locally.
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 39-51
In our following example, we use the simple cell actors shown below as
communication endpoints. The first part of the example illustrates how
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
: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
:language: C++
:lines: 67-69
:start-after: --(rst-spawn-begin)--
:end-before: --(rst-spawn-end)--
When passing the ``cells`` vector to our three different
implementations, we observe three outputs. Our ``waiting_testee`` actor
will always print:
When passing the ``cells`` vector to our three different ``testee``
implementations, we observe three outputs. Our ``waiting_testee`` actor will
always print:
.. ::
.. code-block:: none
cell #9 -> 16
cell #8 -> 9
......@@ -233,7 +222,7 @@ immediately.
Finally, the ``blocking_testee`` implementation will always print:
.. ::
.. code-block:: none
cell #5 -> 0
cell #6 -> 1
......@@ -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
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
~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -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
an error handler as optional second parameter to ``then`` and ``await`` (this
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
scheduled actors.
default error handler (see :ref:`error-message`) get called instead.
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`).
.. literalinclude:: /examples/message_passing/divider.cpp
: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
errors to the user.
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:lines: 70-76
:start-after: --(rst-request-begin)--
:end-before: --(rst-request-end)--
.. _delay-message:
......@@ -279,20 +271,21 @@ illustrated in the following time-based loop example.
.. literalinclude:: /examples/message_passing/dancing_kirby.cpp
:language: C++
:lines: 58-75
:start-after: --(rst-dancing-kirby-begin)--
:end-before: --(rst-dancing-kirby-end)--
.. _delegate:
Delegating Messages
-------------------
Actors can transfer responsibility for a request by using ``delegate``.
This enables the receiver of the delegated message to reply as usual---simply
by returning a value from its message handler---and the original sender of the
message will receive the response. The following diagram illustrates request
delegation from actor B to actor C.
Actors can transfer responsibility for a request by using ``delegate``. This
enables the receiver of the delegated message to reply as usual (by returning a
value from its message handler) and the original sender of the message will
receive the response. The following diagram illustrates request delegation from
actor B to actor C.
.. ::
.. code-block::
A B C
| | |
......@@ -317,7 +310,8 @@ the compiler to check the result type when using statically typed actors.
.. literalinclude:: /examples/message_passing/delegating.cpp
:language: C++
:lines: 15-36
:start-after: --(rst-delegate-begin)--
:end-before: --(rst-delegate-end)--
.. _promise:
......@@ -334,14 +328,14 @@ a promise, an actor can fulfill it by calling the member function
.. literalinclude:: /examples/message_passing/promises.cpp
:language: C++
:lines: 18-43
:start-after: --(rst-promises-begin)--
:end-before: --(rst-promises-end)--
Message Priorities
------------------
By default, all messages have the default priority, i.e.,
``message_priority::normal``. Actors can send urgent messages by
setting the priority explicitly:
``send<message_priority::high>(dst,...)``. Urgent messages are put into
a different queue of the receiver's mailbox. Hence, long wait delays can be
avoided for urgent communication.
``message_priority::normal``. Actors can send urgent messages by setting the
priority explicitly: ``send<message_priority::high>(dst,...)``. Urgent messages
are put into a different queue of the receiver's mailbox to avoid long delays
for urgent communication.
......@@ -92,12 +92,10 @@ connect to the published actor by calling ``remote_actor``:
// node B
auto ping = system.middleman().remote_actor("node A", 4242);
if (! ping) {
cerr << "unable to connect to node A: "
<< system.render(ping.error()) << std::endl;
} else {
if (!ping)
cerr << "unable to connect to node A: " << to_string(ping.error()) << '\n';
else
self->send(*ping, ping_atom::value);
}
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
......
......@@ -4,23 +4,32 @@ Overview
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:
.. ::
.. code-block:: none
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
make -C build
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)
your compiler version, (b) your OS, and (c) the content of the file
``build/Testing/Temporary/LastTest.log``.
The ``configure`` script provides a convenient way for creating a build
directory and calling CMake. Users that are familiar with CMake can of course
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
--------
......
......@@ -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
setting.
+-------------------------------------------+-------------------------------------------------+
| **Types** | |
+-------------------------------------------+-------------------------------------------------+
+---------------------------------------+-------------------------------------------------+
| **Types** |
+---------------------------------------+-------------------------------------------------+
| ``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(atom_value)`` | Returns the actor associated to given name. |
+-------------------------------------------+-------------------------------------------------+
+---------------------------------------+-------------------------------------------------+
| ``name_map named_actors()`` | Returns all name mappings. |
+-------------------------------------------+-------------------------------------------------+
+---------------------------------------+-------------------------------------------------+
| ``size_t running()`` | Returns the number of currently running actors. |
+-------------------------------------------+-------------------------------------------------+
| | |
+-------------------------------------------+-------------------------------------------------+
| **Modifiers** | |
+-------------------------------------------+-------------------------------------------------+
| ``void put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. |
+-------------------------------------------+-------------------------------------------------+
| ``void erase(actor_id)`` | Removes an ID 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. |
+-------------------------------------------+-------------------------------------------------+
+---------------------------------------+-------------------------------------------------+
| **Modifiers** |
+---------------------------------------+-------------------------------------------------+
| ``put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. |
+---------------------------------------+-------------------------------------------------+
| ``erase(actor_id)`` | Removes an ID mapping from the registry. |
+---------------------------------------+-------------------------------------------------+
| ``put(atom_value, strong_actor_ptr)`` | Maps an actor to a name. |
+---------------------------------------+-------------------------------------------------+
| ``erase(atom_value)`` | Removes a name mapping from the registry. |
+---------------------------------------+-------------------------------------------------+
......@@ -10,7 +10,8 @@ named "calculator".
.. literalinclude:: /examples/remoting/remote_spawn.cpp
: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,
``connect`` returns the node ID we need for ``remote_spawn``. This requires the
......
......@@ -69,48 +69,50 @@ Defining Sources
.. literalinclude:: /examples/streaming/integer_stream.cpp
: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
``attach_stream_source`` function and pass it four arguments: a pointer
to *self*, *initializer* for the state, *generator* for
producing values, and *predicate* for signaling the end of the stream.
The simplest way to defining a source is to use the ``attach_stream_source``
function and pass it four arguments: a pointer to *self*, *initializer* for the
state, *generator* for producing values, and *predicate* for signaling the end
of the stream.
Defining Stages
---------------
.. literalinclude:: /examples/streaming/integer_stream.cpp
: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 received input stream handshake as first argument. Instead of a predicate,
``make_stage`` only takes a finalizer, since the stage does not produce
data on its own and a stream terminates if no more sources exist.
The function ``make_stage`` also takes three lambdas but additionally the
received input stream handshake as first argument. Instead of a predicate,
``make_stage`` only takes a finalizer, since the stage does not produce data on
its own and a stream terminates if no more sources exist.
Defining Sinks
--------------
.. literalinclude:: /examples/streaming/integer_stream.cpp
: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
that is does not produce outputs.
The function ``make_sink`` is similar to ``make_stage``, except that is does not
produce outputs.
Initiating Streams
------------------
.. literalinclude:: /examples/streaming/integer_stream.cpp
: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
``int_sink`` with an optional stage ``int_selector``. Sending
``open_atom`` to the source initiates the stream and the source will
respond with a stream handshake.
In our example, we always have a source ``int_source`` and a sink ``int_sink``
with an optional stage ``int_selector``. Sending ``open_atom`` to the source
initiates the stream and the source will respond with a stream handshake.
Using the actor composition in CAF (``snk * src`` reads *sink
after source*) allows us to redirect the stream handshake we send in
``caf_main`` to the sink (or to the stage and then from the stage to
the sink).
Using the actor composition in CAF (``snk * src`` reads *sink after source*)
allows us to redirect the stream handshake we send in ``caf_main`` to the sink
(or to the stage and then from the stage to the sink).
......@@ -99,7 +99,7 @@ as ``CAF_CHECK`` and provide tests rather than implementing a
},
[&](caf::error& err) {
// Must not happen, stop test.
CAF_FAIL(sys.render(err));
CAF_FAIL(err);
});
}
......@@ -127,18 +127,16 @@ configuration classes.
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.
* ``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.
- ``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.
- ``disallow`` aborts the test if a particular message was delivered to an actor.
The following example implements two actors, ``ping`` and
``pong``, that exchange a configurable amount of messages. The test
*three pings* then checks the contents of each message with
``expect`` and verifies that no additional messages exist using
``disallow``.
The following example implements two actors, ``ping`` and ``pong``, that
exchange a configurable amount of messages. The test *three pings* then checks
the contents of each message with ``expect`` and verifies that no additional
messages exist using ``disallow``.
.. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++
:lines: 12-60
:start-after: --(rst-ping-pong-begin)--
:end-before: --(rst-ping-pong-end)--
.. _type-inspection:
Type Inspection (Serialization and String Conversion)
=====================================================
Type Inspection
===============
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
startup (see :ref:`add-custom-message-type`). Using a message type that is not
serializable causes a compiler error (see :ref:`unsafe-message-type`). CAF
serializes individual elements of a message by using the inspection API. This
API allows users to provide code for serialization as well as string conversion
be serializable and need a platform-neutral, unique name (see
:ref:`add-custom-message-type`). Using a message type that is not serializable
causes a compiler error (see :ref:`unsafe-message-type`). CAF serializes
individual elements of a message by using the inspection API. This API allows
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
follows:
......@@ -21,38 +21,41 @@ follows:
The function ``inspect`` passes meta information and data fields to the
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
: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
(1) ``std::tuple``, (2) ``std::pair``, (3) C arrays, (4) any
container type with ``x.size()``, ``x.empty()``,
``x.begin()`` and ``x.end()``.
``std::tuple``, ``std::pair``, arrays, as well as containers that provide the
member functions ``size``, ``empty``, ``begin`` and ``end``.
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
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
-----------------
The following concept class shows the requirements for inspectors. The
placeholder ``T`` represents any user-defined type. For example,
``error`` when performing I/O operations or some integer type when
implementing a hash function.
placeholder ``T`` represents any user-defined type. Usually ``error`` or
``error_code``.
.. code-block:: C++
Inspector {
using result_type = T;
if (inspector only requires read access to the state of T)
static constexpr bool reads_state = true;
else
static constexpr bool writes_state = true;
static constexpr bool reads_state = ...;
static constexpr bool writes_state = ...;
template <class... Ts>
result_type operator()(Ts&&...);
......@@ -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
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
-----------
......@@ -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
all annotations supported by CAF:
* ``type_name(n)``: Display type name as ``n`` in human-friendly output (position before data fields).
* ``hex_formatted()``: Format the following data field in hex format.
* ``omittable()``: Omit the following data field in human-friendly output.
* ``omittable_if_empty()``: Omit the following data field if it is empty in human-friendly output.
* ``omittable_if_none()``: Omit the following data field if it equals ``none`` in human-friendly output.
* ``save_callback(f)``: Call ``f`` when serializing (position after data fields).
* ``load_callback(f)``: Call ``f`` after deserializing all data fields (position after data fields).
- ``type_name(n)``: Display type name as ``n`` in human-friendly output
(position before data fields).
- ``hex_formatted()``: Format the following data field in hex format.
- ``omittable()``: Omit the following data field in human-friendly output.
- ``omittable_if_empty()``: Omit the following data field in human-friendly
output if it is empty.
- ``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
---------------------------------------
CAF evaluates common free function other than ``inspect`` in order to
simplify users to integrate CAF into existing code bases.
CAF evaluates common free function other than ``inspect`` in order to simplify
users to integrate CAF into existing code bases.
Serializers and deserializers call user-defined ``serialize``
functions. Both types support ``operator&`` as well as
``operator()`` for individual data fields. A ``serialize``
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``.
Serializers and deserializers call user-defined ``serialize`` functions. Both
types support ``operator&`` as well as ``operator()`` for individual data
fields. A ``serialize`` function has priority over ``inspect``.
.. _unsafe-message-type:
......@@ -102,32 +113,57 @@ Whitelisting Unsafe Message Types
---------------------------------
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
suppressed by whitelisting types with
``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The macro is defined as follows.
actor communication. For messages that never cross the network, this errors can
be suppressed by whitelisting types with ``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The
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
query whether the inspector is loading or storing. For example, consider the
following class ``foo`` with getter and setter functions and no public
access to its members.
Many classes shield their member variables with getter and setter functions.
This can be addressed by declaring the ``inspect`` function as ``friend``, but
only when not dealing with 3rd party library types. For example, consider the
following class ``foo`` with getter and setter functions and no public access to
its members.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp
: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_``
(and assuming no changes to ``foo`` are possible), we need to split our
implementation of ``inspect`` as shown below.
Since there is no access to the data fields ``a_`` and ``b_`` (and assuming no
changes to ``foo`` are possible), we can serialize or deserialize from/to local
variables and use a load callback to write back to the object when loading
state.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp
: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
the temporaries back to ``foo`` at scope exit automatically. Storing
the result of ``f(...)`` in a temporary first and then writing the
changes to ``foo`` is not possible, because ``f(...)`` can
return ``void``.
For more complicated cases, we can also split the inspect overload as follows:
.. code-block:: C++
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