Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
23182f0d
Commit
23182f0d
authored
Jan 09, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update documentation and examples
parent
c6815ed0
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
390 additions
and
614 deletions
+390
-614
doc/graffle/mailbox_element.graffle
doc/graffle/mailbox_element.graffle
+0
-0
examples/CMakeLists.txt
examples/CMakeLists.txt
+1
-1
examples/aout.cpp
examples/aout.cpp
+23
-20
examples/broker/simple_broker.cpp
examples/broker/simple_broker.cpp
+2
-2
examples/custom_type/custom_types_1.cpp
examples/custom_type/custom_types_1.cpp
+0
-3
examples/hello_world.cpp
examples/hello_world.cpp
+19
-19
examples/message_passing/calculator.cpp
examples/message_passing/calculator.cpp
+18
-11
examples/message_passing/cell.cpp
examples/message_passing/cell.cpp
+2
-2
examples/message_passing/dancing_kirby.cpp
examples/message_passing/dancing_kirby.cpp
+18
-14
examples/message_passing/delegating.cpp
examples/message_passing/delegating.cpp
+19
-22
examples/message_passing/divider.cpp
examples/message_passing/divider.cpp
+4
-0
examples/message_passing/fan_out_request.cpp
examples/message_passing/fan_out_request.cpp
+2
-4
examples/message_passing/promises.cpp
examples/message_passing/promises.cpp
+22
-28
examples/message_passing/request.cpp
examples/message_passing/request.cpp
+31
-16
examples/testing/ping_pong.cpp
examples/testing/ping_pong.cpp
+2
-2
libcaf_core/caf/all.hpp
libcaf_core/caf/all.hpp
+1
-0
libcaf_core/caf/caf_main.hpp
libcaf_core/caf/caf_main.hpp
+24
-0
libcaf_core/caf/exit_reason.hpp
libcaf_core/caf/exit_reason.hpp
+2
-0
libcaf_core/caf/sec.hpp
libcaf_core/caf/sec.hpp
+2
-0
libcaf_core/caf/typed_actor.hpp
libcaf_core/caf/typed_actor.hpp
+24
-16
manual/Actors.rst
manual/Actors.rst
+20
-7
manual/Error.rst
manual/Error.rst
+30
-26
manual/FAQ.rst
manual/FAQ.rst
+2
-16
manual/GroupCommunication.rst
manual/GroupCommunication.rst
+2
-4
manual/ManagingGroupsOfWorkers.rst
manual/ManagingGroupsOfWorkers.rst
+3
-4
manual/MessageHandlers.rst
manual/MessageHandlers.rst
+18
-33
manual/MessagePassing.rst
manual/MessagePassing.rst
+86
-86
manual/Messages.rst
manual/Messages.rst
+0
-253
manual/Overview.rst
manual/Overview.rst
+11
-23
manual/Testing.rst
manual/Testing.rst
+2
-1
manual/index.rst
manual/index.rst
+0
-1
manual/mailbox_element.png
manual/mailbox_element.png
+0
-0
No files found.
doc/graffle/mailbox_element.graffle
View file @
23182f0d
No preview for this file type
examples/CMakeLists.txt
View file @
23182f0d
...
...
@@ -45,7 +45,7 @@ add_core_example(custom_type custom_types_3)
# testing DSL
add_example
(
testing ping_pong
)
target_link_libraries
(
ping_pong PRIVATE CAF::internal CAF::core CAF::test
)
add_test
(
NAME
"examples.ping-pong"
COMMAND ping_pong -r300 -n -v5
)
# -- examples for CAF::io ------------------------------------------------------
...
...
examples/aout.cpp
View file @
23182f0d
#include <random>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <random>
#include "caf/all.hpp"
#include "caf/actor_ostream.hpp"
#include "caf/actor_system.hpp"
#include "caf/caf_main.hpp"
#include "caf/event_based_actor.hpp"
using
namespace
caf
;
using
std
::
endl
;
void
caf_main
(
actor_system
&
system
)
{
for
(
int
i
=
1
;
i
<=
50
;
++
i
)
{
system
.
spawn
([
i
](
blocking_actor
*
self
)
{
aout
(
self
)
<<
"Hi there! This is actor nr. "
<<
i
<<
"!"
<<
endl
;
std
::
random_device
rd
;
std
::
default_random_engine
re
(
rd
());
std
::
chrono
::
milliseconds
tout
{
re
()
%
10
};
self
->
delayed_send
(
self
,
tout
,
42
);
self
->
receive
(
[
i
,
self
](
int
)
{
aout
(
self
)
<<
"Actor nr. "
<<
i
<<
" says goodbye!"
<<
endl
;
}
);
});
}
behavior
printer
(
event_based_actor
*
self
,
int32_t
num
,
int32_t
delay
)
{
aout
(
self
)
<<
"Hi there! This is actor nr. "
<<
num
<<
"!"
<<
std
::
endl
;
std
::
chrono
::
milliseconds
timeout
{
delay
};
self
->
delayed_send
(
self
,
timeout
,
timeout_atom_v
);
return
{
[
=
](
timeout_atom
)
{
aout
(
self
)
<<
"Actor nr. "
<<
num
<<
" says goodbye after waiting for "
<<
delay
<<
"ms!"
<<
std
::
endl
;
},
};
}
void
caf_main
(
actor_system
&
sys
)
{
std
::
random_device
rd
;
std
::
minstd_rand
re
(
rd
());
std
::
uniform_int_distribution
<
int32_t
>
dis
{
1
,
99
};
for
(
int32_t
i
=
1
;
i
<=
50
;
++
i
)
sys
.
spawn
(
printer
,
i
,
dis
(
re
));
}
CAF_MAIN
()
examples/broker/simple_broker.cpp
View file @
23182f0d
...
...
@@ -7,8 +7,6 @@
* - simple_broker -c localhost 4242 *
\******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
#include "caf/config.hpp"
#ifdef CAF_WINDOWS
...
...
@@ -38,12 +36,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
,
...
...
examples/custom_type/custom_types_1.cpp
View file @
23182f0d
// 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>
...
...
examples/hello_world.cpp
View file @
23182f0d
#include <string>
#include <iostream>
#include "caf/a
ll
.hpp"
using
std
::
endl
;
using
std
::
string
;
#include "caf/a
ctor_ostream
.hpp"
#include "caf/actor_system.hpp"
#include "caf/caf_main.hpp"
#include "caf/event_based_actor.hpp"
using
namespace
caf
;
...
...
@@ -13,32 +13,32 @@ behavior mirror(event_based_actor* self) {
return
{
// a handler for messages containing a single string
// that replies with a string
[
=
](
const
st
ring
&
what
)
->
string
{
[
=
](
const
st
d
::
string
&
what
)
->
std
::
string
{
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout
(
self
)
<<
what
<<
endl
;
aout
(
self
)
<<
what
<<
std
::
endl
;
// reply "!dlroW olleH"
return
st
ring
(
what
.
rbegin
(),
what
.
rend
())
;
}
return
st
d
::
string
{
what
.
rbegin
(),
what
.
rend
()}
;
}
,
};
}
void
hello_world
(
event_based_actor
*
self
,
const
actor
&
buddy
)
{
// send "Hello World!" to our buddy ...
self
->
request
(
buddy
,
std
::
chrono
::
seconds
(
10
),
"Hello World!"
)
.
then
(
// ... wait up to 10s for a response ...
[
=
](
const
string
&
what
)
{
// ... and print it
aout
(
self
)
<<
what
<<
endl
;
}
);
self
->
request
(
buddy
,
std
::
chrono
::
seconds
(
10
),
"Hello World!"
)
.
then
(
// ... wait up to 10s for a response ...
[
=
](
const
std
::
string
&
what
)
{
// ... and print it
aout
(
self
)
<<
what
<<
std
::
endl
;
}
);
}
void
caf_main
(
actor_system
&
sys
tem
)
{
void
caf_main
(
actor_system
&
sys
)
{
// create a new actor that calls 'mirror()'
auto
mirror_actor
=
sys
tem
.
spawn
(
mirror
);
auto
mirror_actor
=
sys
.
spawn
(
mirror
);
// create another actor that calls 'hello_world(mirror_actor)';
sys
tem
.
spawn
(
hello_world
,
mirror_actor
);
//
system will wait until both actors are destroyed before leaving main
sys
.
spawn
(
hello_world
,
mirror_actor
);
//
the system will wait until both actors are done before exiting the program
}
// creates a main function for us that calls our caf_main
...
...
examples/message_passing/calculator.cpp
View file @
23182f0d
...
...
@@ -3,8 +3,6 @@
* for both the blocking and the event-based API. *
\******************************************************************************/
// Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
#include <iostream>
#include "caf/all.hpp"
...
...
@@ -14,18 +12,22 @@ using namespace caf;
namespace
{
// --(rst-calculator-actor-begin)--
using
calculator_actor
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
),
result
<
int32_t
>
(
sub_atom
,
int32_t
,
int32_t
)
>
;
// --(rst-calculator-actor-end)--
//
prototypes and forward declarations
//
--(rst-prototypes-begin)--
behavior
calculator_fun
(
event_based_actor
*
self
);
void
blocking_calculator_fun
(
blocking_actor
*
self
);
calculator_actor
::
behavior_type
typed_calculator_fun
();
class
calculator
;
class
blocking_calculator
;
class
typed_calculator
;
// --(rst-prototypes-end)--
// --(rst-function-based-begin)--
// function-based, dynamically typed, event-based API
behavior
calculator_fun
(
event_based_actor
*
)
{
return
{
...
...
@@ -55,7 +57,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
[](
sub_atom
,
int32_t
a
,
int32_t
b
)
{
return
a
-
b
;
},
};
}
// --(rst-function-based-end)--
// --(rst-class-based-begin)--
// class-based, dynamically typed, event-based API
class
calculator
:
public
event_based_actor
{
public:
...
...
@@ -91,6 +95,7 @@ public:
return
typed_calculator_fun
();
}
};
// --(rst-class-based-end)--
void
tester
(
scoped_actor
&
)
{
// end of recursion
...
...
@@ -120,14 +125,16 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
tester
(
self
,
std
::
forward
<
Ts
>
(
xs
)...);
}
void
caf_main
(
actor_system
&
system
)
{
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
>
();
scoped_actor
self
{
system
};
void
caf_main
(
actor_system
&
sys
)
{
// --(rst-spawn-begin)--
auto
a1
=
sys
.
spawn
(
blocking_calculator_fun
);
auto
a2
=
sys
.
spawn
(
calculator_fun
);
auto
a3
=
sys
.
spawn
(
typed_calculator_fun
);
auto
a4
=
sys
.
spawn
<
blocking_calculator
>
();
auto
a5
=
sys
.
spawn
<
calculator
>
();
auto
a6
=
sys
.
spawn
<
typed_calculator
>
();
// --(rst-spawn-end)--
scoped_actor
self
{
sys
};
tester
(
self
,
a1
,
1
,
2
,
a2
,
3
,
4
,
a3
,
5
,
6
,
a4
,
7
,
8
,
a5
,
9
,
10
,
a6
,
11
,
12
);
self
->
send_exit
(
a1
,
exit_reason
::
user_shutdown
);
self
->
send_exit
(
a4
,
exit_reason
::
user_shutdown
);
...
...
examples/message_passing/cell.cpp
View file @
23182f0d
...
...
@@ -40,7 +40,7 @@ behavior unchecked_cell(stateful_actor<cell_state>* self) {
// --(rst-cell-end)--
void
caf_main
(
actor_system
&
system
)
{
// --(rst-spawn-cell-
end
)--
// --(rst-spawn-cell-
begin
)--
// Create one cell for each implementation.
auto
cell1
=
system
.
spawn
(
type_checked_cell
);
auto
cell2
=
system
.
spawn
(
unchecked_cell
);
...
...
@@ -50,7 +50,7 @@ void caf_main(actor_system& system) {
f
(
put_atom_v
,
20
);
cout
<<
"cell value (after setting to 20): "
<<
f
(
get_atom_v
)
<<
endl
;
// Get an unchecked cell and send it some garbage. Triggers an "unexpected
// message" error.
// message" error
(and terminates cell2!)
.
anon_send
(
cell2
,
"hello there!"
);
}
...
...
examples/message_passing/dancing_kirby.cpp
View file @
23182f0d
...
...
@@ -55,24 +55,28 @@ void draw_kirby(const animation_step& animation) {
cout
.
flush
();
}
// --(rst-delayed-send-begin)--
// uses a message-based loop to iterate over all animation steps
void
dancing_kirby
(
event_based_actor
*
self
)
{
behavior
dancing_kirby
(
event_based_actor
*
self
)
{
using
namespace
std
::
literals
::
chrono_literals
;
// 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)
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
);
});
return
{
[
=
](
update_atom
,
size_t
step
)
{
if
(
step
==
sizeof
(
animation_step
))
{
// we've printed all animation steps (done)
cout
<<
endl
;
self
->
quit
();
return
;
}
// print given step
draw_kirby
(
animation_steps
[
step
]);
// schedule next animation step
self
->
delayed_send
(
self
,
150ms
,
update_atom_v
,
step
+
1
);
},
};
}
// --(rst-delayed-send-end)--
void
caf_main
(
actor_system
&
system
)
{
system
.
spawn
(
dancing_kirby
);
...
...
examples/message_passing/delegating.cpp
View file @
23182f0d
#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)
using
calc
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
// --(rst-delegate-begin)--
using
adder_actor
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
void
actor_a
(
event_based_actor
*
self
,
const
calc
&
worker
)
{
self
->
request
(
worker
,
std
::
chrono
::
seconds
(
10
),
add_atom_v
,
1
,
2
)
.
then
([
=
](
int32_t
result
)
{
//
aout
(
self
)
<<
"1 + 2 = "
<<
result
<<
std
::
endl
;
});
adder_actor
::
behavior_type
worker_impl
()
{
return
{
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
};
}
calc
::
behavior_type
actor_b
(
calc
::
pointer
self
,
const
calc
&
worker
)
{
adder_actor
::
behavior_type
server_impl
(
adder_actor
::
pointer
self
,
adder_actor
worker
)
{
return
{
[
=
](
add_atom
add
,
int32_t
x
,
int32_t
y
)
{
return
self
->
delegate
(
worker
,
add
,
x
,
y
);
...
...
@@ -27,14 +19,19 @@ calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
};
}
calc
::
behavior_type
actor_c
()
{
return
{
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
};
void
client_impl
(
event_based_actor
*
self
,
adder_actor
adder
,
int32_t
x
,
int32_t
y
)
{
using
namespace
std
::
literals
::
chrono_literals
;
self
->
request
(
adder
,
10s
,
add_atom_v
,
x
,
y
).
then
([
=
](
int32_t
result
)
{
aout
(
self
)
<<
x
<<
" + "
<<
y
<<
" = "
<<
result
<<
std
::
endl
;
});
}
void
caf_main
(
actor_system
&
system
)
{
system
.
spawn
(
actor_a
,
system
.
spawn
(
actor_b
,
system
.
spawn
(
actor_c
)));
void
caf_main
(
actor_system
&
sys
)
{
auto
worker
=
sys
.
spawn
(
worker_impl
);
auto
server
=
sys
.
spawn
(
server_impl
,
sys
.
spawn
(
worker_impl
));
sys
.
spawn
(
client_impl
,
server
,
1
,
2
);
}
// --(rst-delegate-end)--
CAF_MAIN
()
examples/message_passing/divider.cpp
View file @
23182f0d
...
...
@@ -35,6 +35,7 @@ CAF_END_TYPE_ID_BLOCK(divider)
CAF_ERROR_CODE_ENUM
(
math_error
)
// --(rst-divider-begin)--
using
divider
=
typed_actor
<
result
<
double
>
(
div_atom
,
double
,
double
)
>
;
divider
::
behavior_type
divider_impl
()
{
...
...
@@ -46,6 +47,7 @@ divider::behavior_type divider_impl() {
},
};
}
// --(rst-divider-end)--
void
caf_main
(
actor_system
&
system
)
{
double
x
;
...
...
@@ -54,6 +56,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 +66,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
)
examples/message_passing/fan_out_request.cpp
View file @
23182f0d
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 86-98 (MessagePassing.tex)
#include <cassert>
#include <chrono>
#include <iomanip>
...
...
@@ -101,6 +97,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
[
=
](
error
&
err
)
mutable
{
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
// --(rst-fan-out-begin)--
[
=
](
get_atom
get
,
average_atom
,
column_atom
,
int
column
)
{
assert
(
column
<
columns
);
std
::
vector
<
cell
>
columns
;
...
...
@@ -118,6 +115,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
[
=
](
error
&
err
)
mutable
{
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
// --(rst-fan-out-end)--
};
}
...
...
examples/message_passing/promises.cpp
View file @
23182f0d
/******************************************************************************\
* Illustrates response promises. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-43 (MessagePassing.tex)
#include <iostream>
#include "caf/all.hpp"
using
std
::
cout
;
using
std
::
endl
;
using
namespace
caf
;
using
adder
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
// --(rst-promise-begin)--
using
adder_actor
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
// function-based, statically typed, event-based API
adder
::
behavior_type
worker
()
{
adder_actor
::
behavior_type
worker_impl
()
{
return
{
[](
add_atom
,
int32_t
a
,
int32_t
b
)
{
return
a
+
b
;
},
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
};
}
// function-based, statically typed, event-based API
adder
::
behavior_type
calculator_master
(
adder
::
pointer
self
)
{
auto
w
=
self
->
spawn
(
worker
);
adder_actor
::
behavior_type
server_impl
(
adder_actor
::
pointer
self
,
adder_actor
worker
)
{
return
{
[
=
](
add_atom
x
,
int32_t
y
,
int32_t
z
)
->
result
<
int32_t
>
{
[
=
](
add_atom
,
int32_t
y
,
int32_t
z
)
{
auto
rp
=
self
->
make_response_promise
<
int32_t
>
();
self
->
request
(
w
,
infinite
,
x
,
y
,
z
).
then
([
=
](
int32_t
result
)
mutable
{
rp
.
deliver
(
result
);
});
self
->
request
(
worker
,
infinite
,
add_atom_v
,
y
,
z
)
.
then
([
rp
](
int32_t
result
)
mutable
{
rp
.
deliver
(
result
);
});
return
rp
;
},
};
}
void
caf_main
(
actor_system
&
system
)
{
auto
f
=
make_function_view
(
system
.
spawn
(
calculator_master
));
cout
<<
"12 + 13 = "
<<
f
(
add_atom_v
,
12
,
13
)
<<
endl
;
void
client_impl
(
event_based_actor
*
self
,
adder_actor
adder
,
int32_t
x
,
int32_t
y
)
{
using
namespace
std
::
literals
::
chrono_literals
;
self
->
request
(
adder
,
10s
,
add_atom_v
,
x
,
y
).
then
([
=
](
int32_t
result
)
{
aout
(
self
)
<<
x
<<
" + "
<<
y
<<
" = "
<<
result
<<
std
::
endl
;
});
}
void
caf_main
(
actor_system
&
sys
)
{
auto
worker
=
sys
.
spawn
(
worker_impl
);
auto
server
=
sys
.
spawn
(
server_impl
,
sys
.
spawn
(
worker_impl
));
sys
.
spawn
(
client_impl
,
server
,
1
,
2
);
}
// --(rst-promise-end)--
CAF_MAIN
()
examples/message_passing/request.cpp
View file @
23182f0d
...
...
@@ -2,10 +2,6 @@
* Illustrates semantics of request().{then|await|receive}. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <chrono>
#include <cstdint>
#include <iostream>
...
...
@@ -18,22 +14,38 @@ using std::vector;
using
std
::
chrono
::
seconds
;
using
namespace
caf
;
// --(rst-cell-begin)--
using
cell
=
typed_actor
<
result
<
void
>
(
put_atom
,
int32_t
),
result
<
int32_t
>
(
get_atom
)
>
;
=
typed_actor
<
result
<
void
>
(
put_atom
,
int32_t
),
// 'put' writes to the cell
result
<
int32_t
>
(
get_atom
)
>
;
// 'get 'reads from the cell
struct
cell_state
{
int32_t
value
=
0
;
static
constexpr
inline
const
char
*
name
=
"cell"
;
cell
::
pointer
self
;
int32_t
value
;
cell_state
(
cell
::
pointer
ptr
,
int32_t
val
)
:
self
(
ptr
),
value
(
val
)
{
// nop
}
cell_state
(
const
cell_state
&
)
=
delete
;
cell_state
&
operator
=
(
const
cell_state
&
)
=
delete
;
cell
::
behavior_type
make_behavior
()
{
return
{
[
=
](
put_atom
,
int32_t
val
)
{
value
=
val
;
},
[
=
](
get_atom
)
{
return
value
;
},
};
}
};
cell
::
behavior_type
cell_impl
(
cell
::
stateful_pointer
<
cell_state
>
self
,
int32_t
x0
)
{
self
->
state
.
value
=
x0
;
return
{
[
=
](
put_atom
,
int32_t
val
)
{
self
->
state
.
value
=
val
;
},
[
=
](
get_atom
)
{
return
self
->
state
.
value
;
},
};
}
using
cell_impl
=
cell
::
stateful_impl
<
cell_state
>
;
// --(rst-cell-end)--
// --(rst-testees-begin)--
void
waiting_testee
(
event_based_actor
*
self
,
vector
<
cell
>
cells
)
{
for
(
auto
&
x
:
cells
)
self
->
request
(
x
,
seconds
(
1
),
get_atom_v
).
await
([
=
](
int32_t
y
)
{
...
...
@@ -59,11 +71,13 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
aout
(
self
)
<<
"cell #"
<<
x
.
id
()
<<
" -> "
<<
to_string
(
err
)
<<
endl
;
});
}
// --(rst-testees-end)--
// --(rst-main-begin)--
void
caf_main
(
actor_system
&
system
)
{
vector
<
cell
>
cells
;
for
(
auto
i
=
0
;
i
<
5
;
++
i
)
cells
.
emplace_back
(
system
.
spawn
(
cell_impl
,
i
*
i
));
for
(
int32_t
i
=
0
;
i
<
5
;
++
i
)
cells
.
emplace_back
(
system
.
spawn
<
cell_impl
>
(
i
*
i
));
scoped_actor
self
{
system
};
aout
(
self
)
<<
"waiting_testee"
<<
endl
;
auto
x1
=
self
->
spawn
(
waiting_testee
,
cells
);
...
...
@@ -74,5 +88,6 @@ void caf_main(actor_system& system) {
aout
(
self
)
<<
"blocking_testee"
<<
endl
;
system
.
spawn
(
blocking_testee
,
cells
);
}
// --(rst-main-end)--
CAF_MAIN
()
examples/testing/ping_pong.cpp
View file @
23182f0d
// 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)--
libcaf_core/caf/all.hpp
View file @
23182f0d
...
...
@@ -32,6 +32,7 @@
#include "caf/blocking_actor.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/byte_span.hpp"
#include "caf/caf_main.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_value.hpp"
...
...
libcaf_core/caf/caf_main.hpp
0 → 100644
View file @
23182f0d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
// For historic reasons, CAF_MAIN is implemented in exec_main.hpp. Eventually
// the implementation of the macro should move here.
#include "caf/exec_main.hpp"
libcaf_core/caf/exit_reason.hpp
View file @
23182f0d
...
...
@@ -19,6 +19,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.
...
...
@@ -40,6 +41,7 @@ enum class exit_reason : uint8_t {
/// Indicates that an actor was killed because it became unreachable.
unreachable
};
// --(rst-exit-reason-end)--
/// @relates exit_reason
CAF_CORE_EXPORT
std
::
string
to_string
(
exit_reason
);
...
...
libcaf_core/caf/sec.hpp
View file @
23182f0d
...
...
@@ -19,6 +19,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
{
...
...
@@ -159,6 +160,7 @@ enum class sec : uint8_t {
/// A key lookup failed.
no_such_key
=
65
,
};
// --(rst-sec-end)--
/// @relates sec
CAF_CORE_EXPORT
std
::
string
to_string
(
sec
);
...
...
libcaf_core/caf/typed_actor.hpp
View file @
23182f0d
...
...
@@ -54,6 +54,9 @@ public:
// tell actor_cast which semantic this type uses
static
constexpr
bool
has_weak_ptr_semantics
=
false
;
/// Stores the template parameter pack.
using
signatures
=
detail
::
type_list
<
Sigs
...
>
;
/// Creates a new `typed_actor` type by extending this one with `Es...`.
template
<
class
...
Es
>
using
extend
=
typed_actor
<
Sigs
...,
Es
...
>
;
...
...
@@ -68,15 +71,31 @@ public:
/// for their behavior stack.
using
behavior_type
=
typed_behavior
<
Sigs
...
>
;
/// The default, event-based type for implementing this messaging interface.
using
impl
=
typed_event_based_actor
<
Sigs
...
>
;
/// Identifies pointers to instances of this kind of actor.
using
pointer
=
typed_event_based_actor
<
Sigs
...
>
*
;
using
pointer
=
impl
*
;
/// A
llows a view to an actor implementing
this messaging interface without
/// knowledge of the actual type.
.
/// A
view to an actor that implements
this messaging interface without
/// knowledge of the actual type.
using
pointer_view
=
typed_actor_pointer
<
Sigs
...
>
;
/// Identifies the base class for this kind of actor.
using
base
=
typed_event_based_actor
<
Sigs
...
>
;
/// A class type suitable as base type class-based implementations.
using
base
=
impl
;
/// The default, event-based type for implementing this messaging interface as
/// a stateful actor.
template
<
class
State
>
using
stateful_impl
=
stateful_actor
<
State
,
impl
>
;
template
<
class
State
>
using
stateful_base
[[
deprecated
(
"use stateful_impl instead"
)]]
=
stateful_actor
<
State
,
base
>
;
/// Convenience alias for `stateful_impl<State>*`.
template
<
class
State
>
using
stateful_pointer
=
stateful_impl
<
State
>*
;
/// Identifies pointers to brokers implementing this interface.
using
broker_pointer
=
io
::
typed_broker
<
Sigs
...
>*
;
...
...
@@ -84,17 +103,6 @@ public:
/// Identifies the base class of brokers implementing this interface.
using
broker_base
=
io
::
typed_broker
<
Sigs
...
>
;
/// Stores the template parameter pack.
using
signatures
=
detail
::
type_list
<
Sigs
...
>
;
/// Identifies the base class for this kind of actor with actor.
template
<
class
State
>
using
stateful_base
=
stateful_actor
<
State
,
base
>
;
/// Identifies the base class for this kind of actor with actor.
template
<
class
State
>
using
stateful_pointer
=
stateful_actor
<
State
,
base
>*
;
/// Identifies the broker_base class for this kind of actor with actor.
template
<
class
State
>
using
stateful_broker_base
=
stateful_actor
<
State
,
broker_base
>
;
...
...
manual/Actors.rst
View file @
23182f0d
...
...
@@ -250,14 +250,15 @@ messaging interface for a simple calculator.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 17-18
:start-after: --(rst-calculator-actor-begin)--
:end-before: --(rst-calculator-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
alias definition eases refactoring later on.
Interfaces have set semantics. This means the following two type aliases
``i1`` and ``i2`` are
equal
:
``i1`` and ``i2`` are
considered equal by CAF
:
.. code-block:: C++
...
...
@@ -300,13 +301,15 @@ parameter. For example, the following functions and classes represent actors.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 21-26
:start-after: --(rst-prototypes-begin)--
:end-before: --(rst-prototypes-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
...
...
@@ -344,7 +347,8 @@ dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 28-56
:start-after: --(rst-function-based-begin)--
:end-before: --(rst-function-based-end)--
.. _class-based:
...
...
@@ -365,6 +369,14 @@ 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.
The following three classes implement the prototypes shown in spawn_ by
delegating to the function-based implementations we have seen before:
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:start-after: --(rst-class-based-begin)--
:end-before: --(rst-class-based-end)--
.. _stateful-actor:
Stateful Actors
...
...
@@ -405,7 +417,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.
...
...
@@ -461,7 +474,7 @@ before the optional timeout, as shown in the example below.
[&](const exit_msg& x) {
// ...
},
others >> [](message
_view& x) -> result<message>
{
others >> [](message
& x) -> skippable_result
{
// report unexpected message back to client
return sec::unexpected_message;
}
...
...
manual/Error.rst
View file @
23182f0d
...
...
@@ -5,9 +5,7 @@ Errors
Errors in CAF have a code and a category, similar to ``std::error_code`` and
``std::error_condition``. Unlike its counterparts from the C++ standard library,
``error`` is plattform-neutral and serializable. Instead of using category
singletons, CAF stores categories as atoms (see :ref:`atom`). Errors can also
include a message to provide additional context information.
``error`` is plattform-neutral and serializable.
Class Interface
---------------
...
...
@@ -15,19 +13,17 @@ Class Interface
+-----------------------------------------+--------------------------------------------------------------------+
| **Constructors** | |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(Enum
x)`` | Construct error by calling ``make_error(x)``
|
| ``(Enum
code)`` | Constructs an error with given error code.
|
+-----------------------------------------+--------------------------------------------------------------------+
| ``(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`` |
| ``(Enum code, messag context)`` | Constructs an error with given error code and additional context. |
+-----------------------------------------+--------------------------------------------------------------------+
| | |
+-----------------------------------------+--------------------------------------------------------------------+
| **Observers** | |
+-----------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code
|
| ``uint8_t code()`` | Returns the error code
as 8-bit integer.
|
+-----------------------------------------+--------------------------------------------------------------------+
| ``
atom_value category()`` | Returns the error category
|
| ``
type_id_t category()`` | Returns the type ID of the Enum type used to construct this error.
|
+-----------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+
...
...
@@ -39,11 +35,19 @@ Class Interface
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 these steps:
* Declare an enum class of type ``uint8_t`` with error codes starting at 1. CAF
always interprets the value 0 as *no error*.
* Assign a type ID to your enum type.
* Specialize ``caf::is_error_code_enum`` for your enum type. For this step, CAF
offers the macro ``CAF_ERROR_CODE_ENUM`` to generate the boilerplate code
necessary.
The following example illustrates all these steps for a custom error code enum
called ``math_error``.
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
...
...
@@ -51,31 +55,31 @@ The following example adds custom error codes for math errors.
.. _sec:
System
Error Codes
------------------
Default
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.
The enum type ``sec`` (for System Error Code) provides many error codes for
common failures in actor systems:
.. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++
:lines: 32-117
:start-after: --(rst-sec-begin)--
:end-before: --(rst-sec-end)--
.. _exit-reason:
Default Exit Reasons
--------------------
CAF uses the error category ``"exit"`` for default exit reasons. These errors
are
usually fail states set by the actor system itself. The two exceptions are
A special kind of error codes are exit reasons of actors. These errors are
usually fail states set by the actor system itself. The two exceptions are
``exit_reason::user_shutdown`` and ``exit_reason::kill``. The former is used in
CAF to signalize orderly, user-requested shutdown and can be used by programmers
in the same way. The latter terminates an actor unconditionally when used in
``send_exit``, even
if the default handler for exit messages
(see
:ref:`exit-message`)
is overridden
.
``send_exit``, even
for actors that override the default handler
(see
:ref:`exit-message`).
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp
:language: C++
:
lines: 29-49
:
start-after: --(rst-exit-reason-begin)--
:end-before: --(rst-exit-reason-end)--
manual/FAQ.rst
View file @
23182f0d
...
...
@@ -36,19 +36,5 @@ generation, CAF also offers ``message_builder``:
What Debugging Tools Exist?
---------------------------
The ``scripts/`` and ``tools/`` directories contain some useful tools to aid in
development and debugging.
``scripts/atom.py`` converts integer atom values back into strings.
``scripts/demystify.py`` replaces cryptic ``typed_mpi<...>``
templates with ``replies_to<...>::with<...>`` and
``atom_constant<...>`` with a human-readable representation of the
actual atom.
``scripts/caf-prof`` is an R script that generates plots from CAF
profiler output.
``caf-vec`` is a (highly) experimental tool that annotates CAF logs
with vector timestamps. It gives you happens-before relations and a nice
visualization via `ShiViz <https://bestchai.bitbucket.io/shiviz/>`_.
The ``scripts/`` directory contains some useful tools to aid in analyzing CAF
log output.
manual/GroupCommunication.rst
View file @
23182f0d
...
...
@@ -10,10 +10,8 @@ name, joining, and leaving.
.. code-block:: C++
std::string module = "local";
std::string id = "foo";
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
auto expected_grp = system.groups().get("local", "foo");
if (!expected_grp) {
std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
<< std::endl;
return;
...
...
manual/ManagingGroupsOfWorkers.rst
View file @
23182f0d
...
...
@@ -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
...
...
manual/MessageHandlers.rst
View file @
23182f0d
...
...
@@ -17,9 +17,9 @@ change its behavior when not receiving message after a certain amount of time.
.. code-block:: C++
message_handler x1{
[](int i) { /*...*/ },
[](int
32_t
i) { /*...*/ },
[](double db) { /*...*/ },
[](int
a, int b, in
t c) { /*...*/ }
[](int
32_t a, int32_t b, int32_
t c) { /*...*/ }
};
In our first example, ``x1`` models a behavior accepting messages that consist
...
...
@@ -70,48 +70,33 @@ 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.
Atoms in CAF are tag types, i.e., usually defined as en empty ``struct``. These
types carry no data on their own and only exist to annotate messages. For
example, we could create the two tag types ``add_atom`` and ``multiply_atom``
for implementing a simple math actor like this:
.. code-block:: C++
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
CAF_BEGIN_TYPE_ID_BLOCK(my_project, caf::first_custom_type_id)
**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.
CAF_ADD_ATOM(my_project, add_atom)
CAF_ADD_ATOM(my_project, multiply_atom)
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
accomplish this, CAF offers compile-time *atom constants*.
.. code-block:: C++
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
Using these constants, we can now define message passing interfaces in a
convenient way:
.. code-block:: C++
CAF_END_TYPE_ID_BLOCK(my_project)
behavior do_math{
[](add_atom, int
a, in
t b) {
[](add_atom, int
32_t a, int32_
t b) {
return a + b;
},
[](multiply_atom, int
a, in
t b) {
[](multiply_atom, int
32_t a, int32_
t b) {
return a * b;
}
};
// caller side: send(math_actor, add_atom::value, 1, 2)
Atom constants define a static member ``value``. Please note that this
static ``value`` member does *not* have the type
``atom_value``, unlike ``std::integral_constant`` for example.
// caller side: send(math_actor, add_atom_v, int32_t{1}, int32_t{2})
The macro ``CAF_ADD_ATOM`` defined an empty ``struct`` with the given name as
well as a ``constexpr`` variable for conveniently creating a value of that type
that uses the type name plus a ``_v`` suffix. In the example above,
``atom_value`` is the type name and ``atom_value_v`` is the constant.
manual/MessagePassing.rst
View file @
23182f0d
...
...
@@ -3,18 +3,12 @@
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.
The messaging layer of CAF has three primitives for sending messages: ``send``,
``request``, and ``delegate``. The former simply enqueues a message to the
mailbox the receiver. The latter two are discussed in more detail in
:ref:`request` and :ref:`delegate`.
:ref:`request` and :ref:`delegate`. Before we go into the details of the message
passing API itself, we first discuss the building blocks that enable message
passing in the first place.
.. _mailbox-element:
...
...
@@ -37,17 +31,12 @@ request. The ``stages`` vector stores the path of the message. Response
messages, i.e., the returned values of a message handler, are sent to
``stages.back()`` after calling ``stages.pop_back()``. This allows CAF to build
pipelines of arbitrary size. If no more stage is left, the response reaches the
sender. Finally, ``content()`` grants access to the type-erased tuple storing
the message itself.
sender. Finally, ``payload`` is the actual content of the message.
Mailbox elements are created by CAF automatically and are usually invisible to
the programmer. However, understanding how messages are processed internally
helps understanding the behavior of the message passing layer.
It is worth mentioning that CAF usually wraps the mailbox element and its
content into a single object in order to reduce the number of memory
allocations.
.. _copy-on-write:
Copy on Write
...
...
@@ -65,19 +54,15 @@ Requirements for Message Types
Message types in CAF must meet the following requirements:
1.
Serializable or i
nspectable (see :ref:`type-inspection`)
1.
I
nspectable (see :ref:`type-inspection`)
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&)``.
A type ``T`` is inspectable if it provides a free function
``inspect(Inspector&, T&)`` or specializes ``inspector_access``.
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
:ref:`copy-on-write`).
create an object for ``T`` when deserializing incoming messages. Requirement 3
allows CAF to implement Copy on Write (see :ref:`copy-on-write`).
.. _special-handler:
...
...
@@ -158,12 +143,12 @@ Requests
--------
A main feature of CAF is its ability to couple input and output types via the
type system. For example, a ``typed_actor<re
plies_to<int>::with<int>
>``
essentially behaves like a function. It receives a single ``int
`` as
input and responds with another ``int``. CAF embraces this functional
take on actors by simply creating response messages from the result of message
handlers. This allows CAF to match *request* to *response* messages
and to provide a
convenient API for this style of communication.
type system. For example, a ``typed_actor<re
sult<int32_t>(int32_t)
>``
essentially behaves like a function. It receives a single ``int
32_t`` as input
and responds with another ``int32_t``. CAF embraces this functional take on
actors by simply creating response messages from the result of message handlers.
This allows CAF to match *request* to *response* messages and to provide a
convenient API for this style of communication.
.. _handling-response:
...
...
@@ -181,42 +166,31 @@ responses arrive and handles requests in LIFO order. Blocking actors always use
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.
known to run
locally.
In our following example, we use the simple cell actor
s
shown below as
communication endpoint
s
.
In our following example, we use the simple cell actor shown below as
communication endpoint.
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 20-37
:start-after: --(rst-cell-begin)--
:end-before: --(rst-cell-end)--
T
he first part of the example illustrates how event-based actors can use either
``then`` or ``await``
.
T
o showcase the slight differences in API and processing order, we implement
three testee actors that all operate on a list of cell actors
.
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 39-51
:start-after: --(rst-testees-begin)--
:end-before: --(rst-testees-end)--
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
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
Our ``caf_main`` for the examples spawns five cells and assign the initial
values 0, 1, 4, 9, and 16. Then it spawns one instance for each of our testee
implementations and we can observe the different outputs.
When passing the ``cells`` vector to our three different
implementations, we observe three outputs. Our ``waiting_testee`` actor
will always print:
Our ``waiting_testee`` actor will always print:
..
::
..
code-block:: none
cell #9 -> 16
cell #8 -> 9
...
...
@@ -225,15 +199,17 @@ will always print:
cell #5 -> 0
This is because ``await`` puts the one-shots handlers onto a stack and
enforces LIFO order by re-ordering incoming response messages.
enforces LIFO order by re-ordering incoming response messages
as necessary
.
The ``multiplexed_testee`` implementation does not print its results in
a predicable order. Response messages arrive in arbitrary order and are handled
immediately.
Finally, the ``blocking_testee`` implementation will always print:
Finally, the ``blocking_testee`` has a deterministic output again. This is
because it blocks on each request until receiving the result before making the
next request.
..
::
..
code-block:: none
cell #5 -> 0
cell #6 -> 1
...
...
@@ -254,17 +230,17 @@ manager fans-out the request to all of its workers and then collects the
results. The function ``fan_out_request`` combined with the merge policy
``select_all`` streamlines this exact use case.
In the following snippet, we have a matrix actor (``self``) that stores
worker actors for each cell (each simply storing an integer). For computing the
average over a row, we use ``fan_out_request``. The result handler
passed to ``then`` now gets called only once with a ``vector``
holding all collected results. Using a response promise promise_ further
allows us to delay responding to the client until we have collected all worker
results.
In the following snippet, we have a matrix actor ``self`` that stores worker
actors for each cell (each simply storing an integer). For computing the average
over a row, we use ``fan_out_request``. The result handler passed to ``then``
now gets called only once with a ``vector`` holding all collected results. Using
a response promise promise_ further allows us to delay responding to the client
until we have collected all worker results.
.. literalinclude:: /examples/message_passing/fan_out_request.cpp
:language: C++
:lines: 86-98
:start-after: --(rst-fan-out-begin)--
:end-before: --(rst-fan-out-end)--
The policy ``select_any`` models a second common use case: sending a
request to multiple receivers but only caring for the first arriving response.
...
...
@@ -286,26 +262,32 @@ 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.
When sending requests to the divider, we
can use a custom error handlers to
report errors to the user like this:
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:lines: 70-76
:start-after: --(rst-request-begin)--
:end-before: --(rst-request-end)--
.. _delay-message:
Delaying Messages
-----------------
Messages can be delayed by using the function ``delayed_send``, as
i
llustrated i
n the following time-based loop example.
Messages can be delayed by using the function ``delayed_send``, as
illustrated
in the following time-based loop example.
.. literalinclude:: /examples/message_passing/dancing_kirby.cpp
:language: C++
:lines: 58-75
:start-after: --(rst-delayed-send-begin)--
:end-before: --(rst-delayed-send-end)--
Delayed send schedules messages based on relative timeouts. For absolute
timeouts, use ``scheduled_send`` instead.
.. _delegate:
...
...
@@ -318,7 +300,7 @@ 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:: none
A B C
| | |
...
...
@@ -330,11 +312,6 @@ delegation from actor B to actor C.
| |<--/
| <-------------(reply)-------------- |
| X
|---\
| | handle
| | response
|<--/
|
X
Returning the result of ``delegate(...)`` from a message handler, as
...
...
@@ -343,7 +320,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:
...
...
@@ -360,14 +338,36 @@ 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-promise-begin)--
:end-before: --(rst-promise-end)--
This example is almost identical to the example for delegating messages.
However, there is a big difference in the flow of messages. In our first
version, the worker (C) directly responded to the client (A). This time, the
worker sends the result to the server (B), which then fulfills the promise and
thereby sends the result to the client:
.. code-block:: none
A B C
| | |
| ---(request)---> | |
| | ---(request)---> |
| | |---\
| | | | compute
| | | | result
| | |<--/
| | <----(reply)---- |
| | X
| <----(reply)---- |
| X
X
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. Hence, long wait
delays can be avoided for urgent communication.
manual/Messages.rst
deleted
100644 → 0
View file @
c6815ed0
.. _message:
Type-Erased Tuples, Messages and Message Views
==============================================
Messages in CAF are stored in type-erased tuples. The actual message type
itself is usually hidden, as actors use pattern matching to decompose messages
automatically. However, the classes ``message`` and
``message_builder`` allow more advanced use cases than only sending
data from one actor to another.
The interface ``type_erased_tuple`` encapsulates access to arbitrary
data. This data can be stored on the heap or on the stack. A
``message`` is a type-erased tuple that is always heap-allocated and
uses copy-on-write semantics. When dealing with "plain" type-erased tuples,
users are required to check if a tuple is referenced by others via
``type_erased_tuple::shared`` before modifying its content.
The convenience class ``message_view`` holds a reference to either a
stack-located ``type_erased_tuple`` or a ``message``. The
content of the data can be access via ``message_view::content`` in both
cases, which returns a ``type_erased_tuple&``. The content of the view
can be forced into a message object by calling
``message_view::move_content_to_message``. This member function either
returns the stored message object or moves the content of a stack-allocated
tuple into a new message.
RTTI and Type Numbers
---------------------
All builtin types in CAF have a non-zero 6-bit *type number*. All
user-defined types are mapped to 0. When querying the run-time type information
(RTTI) for individual message or tuple elements, CAF returns a pair consisting
of an integer and a pointer to ``std::type_info``. The first value is
the 6-bit type number. If the type number is non-zero, the second value is a
pointer to the C++ type info, otherwise the second value is null. Additionally,
CAF generates 32 bit *type tokens*. These tokens are *type hints*
that summarizes all types in a type-erased tuple. Two type-erased tuples are of
different type if they have different type tokens (the reverse is not true).
Class ``type_erased_tuple``
---------------------------
**Note**: Calling modifiers on a shared type-erased tuple is undefined
behavior.
+----------------------------------------+------------------------------------------------------------+
| **Observers** | |
+----------------------------------------+------------------------------------------------------------+
| ``bool empty()`` | Returns whether this message is empty. |
+----------------------------------------+------------------------------------------------------------+
| ``size_t size()`` | Returns the size of this message. |
+----------------------------------------+------------------------------------------------------------+
| ``rtti_pair type(size_t pos)`` | Returns run-time type information for the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``error save(serializer& x)`` | Writes the tuple to ``x``. |
+----------------------------------------+------------------------------------------------------------+
| ``error save(size_t n, serializer& x)``| Writes the nth element to ``x``. |
+----------------------------------------+------------------------------------------------------------+
| ``const void* get(size_t n)`` | Returns a const pointer to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``std::string stringify()`` | Returns a string representation of the tuple. |
+----------------------------------------+------------------------------------------------------------+
| ``std::string stringify(size_t n)`` | Returns a string representation of the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``bool matches(size_t n, rtti_pair)`` | Checks whether the nth element has given type. |
+----------------------------------------+------------------------------------------------------------+
| ``bool shared()`` | Checks whether more than one reference to the data exists. |
+----------------------------------------+------------------------------------------------------------+
| ``bool match_element<T>(size_t n)`` | Checks whether element ``n`` has type ``T``. |
+----------------------------------------+------------------------------------------------------------+
| ``bool match_elements<Ts...>()`` | Checks whether this message has the types ``Ts...``. |
+----------------------------------------+------------------------------------------------------------+
| ``const T& get_as<T>(size_t n)`` | Returns a const reference to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| | |
+----------------------------------------+------------------------------------------------------------+
| **Modifiers** | |
+----------------------------------------+------------------------------------------------------------+
| ``void* get_mutable(size_t n)`` | Returns a mutable pointer to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``T& get_mutable_as<T>(size_t n)`` | Returns a mutable reference to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``void load(deserializer& x)`` | Reads the tuple from ``x``. |
+----------------------------------------+------------------------------------------------------------+
Class ``message``
-----------------
The class ``message`` includes all member functions of
``type_erased_tuple``. However, calling modifiers is always guaranteed
to be safe. A ``message`` automatically detaches its content by copying
it from the shared data on mutable access. The class further adds the following
member functions over ``type_erased_tuple``. Note that
``apply`` only detaches the content if a callback takes mutable
references as arguments.
+-----------------------------------------------+------------------------------------------------------------+
| **Observers** | |
+-----------------------------------------------+------------------------------------------------------------+
| ``message drop(size_t n)`` | Creates a new message with all but the first ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message drop_right(size_t n)`` | Creates a new message with all but the last ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message take(size_t n)`` | Creates a new message from the first ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message take_right(size_t n)`` | Creates a new message from the last ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message slice(size_t p, size_t n)`` | Creates a new message from ``[p, p + n)``. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message extract(message_handler)`` | See extract_. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message extract_opts(...)`` | See extract-opts_. |
+-----------------------------------------------+------------------------------------------------------------+
| | |
+-----------------------------------------------+------------------------------------------------------------+
| **Modifiers** | |
+-----------------------------------------------+------------------------------------------------------------+
| ``optional<message> apply(message_handler f)``| Returns ``f(*this)``. |
+-----------------------------------------------+------------------------------------------------------------+
| | |
+-----------------------------------------------+------------------------------------------------------------+
| **Operators** | |
+-----------------------------------------------+------------------------------------------------------------+
| ``message operator+(message x, message y)`` | Concatenates ``x`` and ``y``. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message& operator+=(message& x, message y)``| Concatenates ``x`` and ``y``. |
+-----------------------------------------------+------------------------------------------------------------+
Class ``message_builder``
-------------------------
+---------------------------------------------------+-----------------------------------------------------+
| **Constructors** | |
+---------------------------------------------------+-----------------------------------------------------+
| ``(void)`` | Creates an empty message builder. |
+---------------------------------------------------+-----------------------------------------------------+
| ``(Iter first, Iter last)`` | Adds all elements from range ``[first, last)``. |
+---------------------------------------------------+-----------------------------------------------------+
| | |
+---------------------------------------------------+-----------------------------------------------------+
| **Observers** | |
+---------------------------------------------------+-----------------------------------------------------+
| ``bool empty()`` | Returns whether this message is empty. |
+---------------------------------------------------+-----------------------------------------------------+
| ``size_t size()`` | Returns the size of this message. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message to_message( )`` | Converts the buffer to an actual message object. |
+---------------------------------------------------+-----------------------------------------------------+
| ``append(T val)`` | Adds ``val`` to the buffer. |
+---------------------------------------------------+-----------------------------------------------------+
| ``append(Iter first, Iter last)`` | Adds all elements from range ``[first, last)``. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message extract(message_handler)`` | See extract_. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message extract_opts(...)`` | See extract-opts_. |
+---------------------------------------------------+-----------------------------------------------------+
| | |
+---------------------------------------------------+-----------------------------------------------------+
| **Modifiers** | |
+---------------------------------------------------+-----------------------------------------------------+
| ``optional<message>`` ``apply(message_handler f)``| Returns ``f(*this)``. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message move_to_message()`` | Transfers ownership of its data to the new message. |
+---------------------------------------------------+-----------------------------------------------------+
.. _extract:
Extracting
----------
The member function ``message::extract`` removes matched elements from a
message. x Messages are filtered by repeatedly applying a message handler to the
greatest remaining slice, whereas slices are generated in the sequence
``[0, size)``, ``[0, size-1)``, ``...``, ``[1, size-1)``, ``...``,
``[size-1, size)``. Whenever a slice is matched, it is removed from the message
and the next slice starts at the same index on the reduced message.
For example:
.. code-block:: C++
auto msg = make_message(1, 2.f, 3.f, 4);
// remove float and integer pairs
auto msg2 = msg.extract({
[](float, float) { },
[](int, int) { }
});
assert(msg2 == make_message(1, 4));
Step-by-step explanation:
* Slice 1: ``(1, 2.f, 3.f, 4)``, no match
* Slice 2: ``(1, 2.f, 3.f)``, no match
* Slice 3: ``(1, 2.f)``, no match
* Slice 4: ``(1)``, no match
* Slice 5: ``(2.f, 3.f, 4)``, no match
* Slice 6: ``(2.f, 3.f)``, *match*; new message is ``(1, 4)``
* Slice 7: ``(4)``, no match
Slice 7 is ``(4)``, i.e., does not contain the first element, because
the match on slice 6 occurred at index position 1. The function
``extract`` iterates a message only once, from left to right. The
returned message contains the remaining, i.e., unmatched, elements.
.. _extract-opts:
Extracting Command Line Options
-------------------------------
The class ``message`` also contains a convenience interface to
``extract`` for parsing command line options: the member function
``extract_opts``.
.. code-block:: C++
int main(int argc, char** argv) {
uint16_t port;
string host = "localhost";
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"port,p", "set port", port},
{"host,H", "set host (default: localhost)", host},
{"verbose,v", "enable verbose mode"}
});
if (! res.error.empty()) {
// read invalid CLI arguments
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) {
// CLI arguments contained "-h", "--help", or "-?" (builtin);
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// res.remainder stors all extra arguments that weren't consumed
}
if (res.opts.count("verbose") > 0) {
// enable verbose mode
}
// ...
}
/*
Output of ./program_name -h:
Allowed options:
-p [--port] arg : set port
-H [--host] arg : set host (default: localhost)
-v [--verbose] : enable verbose mode
*/
manual/Overview.rst
View file @
23182f0d
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:
Compiling CAF requires CMake and a
recent C++ compiler. To get and compile the
sources on UNIX-like systems, type the following in a terminal:
..
::
..
code-block:: bash
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
install [as root, optional]
We recommended to run the unit tests as well:
.. ::
make test
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``.
Running ``configure`` is not a mandatory step. The script merely automates the
CMake setup and makes setting build options slightly more convenient. On
Windows, use CMake directly to generate an MSVC project file.
Features
--------
...
...
@@ -32,19 +26,13 @@ Features
* Thread-mapped actors for soft migration of existing applications
* Publish/subscribe group communication
Minimal Compiler Versions
-------------------------
* GCC 4.8
* Clang 3.4
* Visual Studio 2015, Update 3
Supported Operating Systems
---------------------------
* Linux
* Mac OS X
* Windows (static library only)
* Windows
* macOS
* FreeBSD
Hello World Example
-------------------
...
...
manual/Testing.rst
View file @
23182f0d
...
...
@@ -139,6 +139,7 @@ The following example implements two actors, ``ping`` and
.. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++
:lines: 12-60
:start-after: --(rst-ping-pong-begin)--
:end-before: --(rst-ping-pong-end)--
manual/index.rst
View file @
23182f0d
...
...
@@ -21,7 +21,6 @@ Contents
ReferenceCounting
Error
ConfiguringActorApplications
Messages
GroupCommunication
ManagingGroupsOfWorkers
Streaming
...
...
manual/mailbox_element.png
View replaced file @
c6815ed0
View file @
23182f0d
20 KB
|
W:
|
H:
39.8 KB
|
W:
|
H:
2-up
Swipe
Onion skin
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment