-
Notifications
You must be signed in to change notification settings - Fork 2
/
ConfiguringActorApplications.tex
158 lines (112 loc) · 8.63 KB
/
ConfiguringActorApplications.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
\section{Configuring Actor Applications}
\label{system-config}
\lib configures applications at startup using an \lstinline^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~\see{system-config-options}.
The following code example is a minimal CAF application with a middleman~\see{middleman} but without any custom configuration options.
\begin{lstlisting}
void caf_main(actor_system& system) {
// ...
}
CAF_MAIN(io::middleman)
\end{lstlisting}
The compiler expands this example code to the following.
\begin{lstlisting}
void caf_main(actor_system& system) {
// ...
}
int main(int argc, char** argv) {
return exec_main<io::middleman>(caf_main, argc, argv);
}
\end{lstlisting}
The function \lstinline^exec_main^ creates a config object, loads all modules requested in \lstinline^CAF_MAIN^ and then calls \lstinline^caf_main^. A minimal implementation for \lstinline^main^ performing all these steps manually is shown in the next example for the sake of completeness.
\begin{lstlisting}
int main(int argc, char** argv) {
actor_system_config cfg;
// read CLI options
cfg.parse(argc, argv);
// 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);
}
\end{lstlisting}
However, setting up config objects by hand is usually not necessary. \lib automatically selects user-defined subclasses of \lstinline^actor_system_config^ if \lstinline^caf_main^ takes a second parameter by reference, as shown in the minimal example below.
\begin{lstlisting}
class my_config : public actor_system_config {
public:
my_config() {
// ...
}
};
void caf_main(actor_system& system, const my_config& cfg) {
// ...
}
CAF_MAIN()
\end{lstlisting}
Users can perform additional initialization, add custom program options, etc. simply by implementing a default constructor.
\subsection{Loading Modules}
\label{system-config-module}
The simplest way to load modules is to use the macro \lstinline^CAF_MAIN^ and to pass a list of all requested modules, as shown below.
\begin{lstlisting}
void caf_main(actor_system& system) {
// ...
}
CAF_MAIN(mod1, mod2, ...)
\end{lstlisting}
Alternatively, users can load modules in user-defined config classes.
\begin{lstlisting}
class my_config : public actor_system_config {
public:
my_config() {
load<mod1>();
load<mod2>();
// ...
}
};
\end{lstlisting}
The third option is to simply call \lstinline^x.load<mod1>()^ on a config object \emph{before} initializing an actor system with it.
\subsection{Command Line Options and INI Configuration Files}
\label{system-config-options}
\lib 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 \lstinline^actor_system_config^. The example below adds three options to the ``global'' category.
\lstinputlisting[linerange={221-233}]{../examples/remoting/distributed_calculator.cpp}
The line \lstinline^opt_group{custom_options_, "global"}^ adds the ``global'' category to the config parser. The following calls to \lstinline^add^ then append individual options to the category. The first argument to \lstinline^add^ is the associated variable. The second argument is the name for the parameter, optionally suffixed with a comma-separated single-character short name. The short name is only considered for CLI parsing and allows users to abbreviate commonly used option names. The third and final argument to \lstinline^add^ is a help text.
The custom \lstinline^config^ class allows end users to set the port for the application to 42 with either \lstinline^--port=42^ (long name) or \lstinline^-p 42^ (short name). The long option name is prefixed by the category when using a different category than ``global''. For example, adding the port option to the category ``foo'' means end users have to type \lstinline^--foo.port=42^ when using the long name. Short names are unaffected by the category, but have to be unique.
Boolean options do not require arguments. The member variable \lstinline^server_mode^ is set to \lstinline^true^ if the command line contains either \lstinline^--server-mode^ or \lstinline^-s^.
\lib prefixes all of its default CLI options with \lstinline^caf#^, except for ``help'' (\lstinline^--help^, \lstinline^-h^, or \lstinline^-?^). The default name for the INI file is \lstinline^caf-application.ini^. Users can change the file name and path by passing \lstinline^--caf#config-file=<path>^ on the command line.
INI files are organized in categories. No value is allowed outside of a category (no implicit ``global'' category). CAF reads \lstinline^true^ and \lstinline^false^ as boolean, numbers as (signed) integers or \lstinline^double^, \lstinline^"^-enclosed characters as strings, and \lstinline^'^-enclosed characters as atoms~\see{atom}. The following example INI file lists all standard options in \lib and their default value. Note that some options such as \lstinline^scheduler.max-threads^ are usually detected at runtime and thus have no hard-coded default.
\clearpage
\lstinputlisting[language=ini]{../examples/caf-application.ini}
\clearpage
\subsection{Adding Custom Message Types}
\label{add-custom-message-type}
\lib requires serialization support for all of its message types \see{type-inspection}. However, \lib 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 \lstinline^foo^.
\lstinputlisting[linerange={24-27}]{../examples/custom_type/custom_types_1.cpp}
To make \lstinline^foo^ serializable, we make it inspectable \see{type-inspection}:
\lstinputlisting[linerange={30-34}]{../examples/custom_type/custom_types_1.cpp}
Finally, we give \lstinline^foo^ a platform-neutral name and add it to the list of serializable types by using a custom config class.
\lstinputlisting[linerange={75-78,81-84}]{../examples/custom_type/custom_types_1.cpp}
\subsection{Adding Custom Error Types}
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 \lstinline^add_error_category^, as shown in~\sref{custom-error}.
\clearpage
\subsection{Adding Custom Actor Types \experimental}
\label{add-custom-actor-type}
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{remote-spawn}. For our example configuration, we consider the following simple \lstinline^calculator^ actor.
\lstinputlisting[linerange={33-39}]{../examples/remoting/remote_spawn.cpp}
Adding the calculator actor type to our config is achieved by calling \lstinline^add_actor_type<T>^. Note that adding an actor type in this way implicitly calls \lstinline^add_message_type<T>^ for typed actors \see{add-custom-message-type}. This makes our \lstinline^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).
\lstinputlisting[linerange={99-101,106-106,110-101}]{../examples/remoting/remote_spawn.cpp}
Our final example illustrates how to spawn a \lstinline^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 \lstinline^spawn^ returns \lstinline^expected<T>^.
\begin{lstlisting}
auto x = system.spawn<calculator>("calculator", make_message());
if (! x) {
std::cerr << "*** unable to spawn calculator: "
<< system.render(x.error()) << std::endl;
return;
}
calculator c = std::move(*x);
\end{lstlisting}
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 simply \lstinline^actor^. For example, spawning an actor "foo" which requires one string is created with \lstinline^system.spawn<actor>("foo", make_message("bar"))^.
Because constructor (or function) arguments for spawning the actor are stored in a \lstinline^message^, only actors with appropriate input types are allowed. For example, \lstinline^const char*^ arguments---or any other pointer type---are not allowed and must be replaced by \lstinline^std::string^.