Commit 41888dc5 authored by Dominik Charousset's avatar Dominik Charousset

Allow typed actors to handle system messages

Strip `down_msg` and `exit_msg` handlers before matching given pattern against
actor interface, close #171.
parent 5b1ab5b0
......@@ -22,6 +22,7 @@
#include "caf/behavior.hpp"
#include "caf/match_expr.hpp"
#include "caf/system_messages.hpp"
#include "caf/typed_continue_helper.hpp"
#include "caf/detail/typed_actor_util.hpp"
......@@ -73,44 +74,69 @@ struct valid_input_predicate {
template <class Expr>
struct inner {
using input_types = typename Expr::input_types;
using output_types = typename unbox_typed_continue_helper<
typename Expr::output_types
>::type;
using output_types =
typename unbox_typed_continue_helper<
typename Expr::output_types
>::type;
// get matching elements for input type
using filtered_slist = typename tl_filter<
SList,
tbind<same_input, input_types>::template type
>::type;
using filtered_slist =
typename tl_filter<
SList,
tbind<same_input, input_types>::template type
>::type;
static_assert(tl_size<filtered_slist>::value > 0,
"cannot assign given match expression to "
"typed behavior, because the expression "
"contains at least one pattern that is "
"not defined in the actor's type");
"cannot assign given match expression to "
"typed behavior, because the expression "
"contains at least one pattern that is "
"not defined in the actor's type");
static constexpr bool value = tl_exists<
filtered_slist, tbind<same_output_or_skip_message_t,
output_types>::template type>::value;
output_types>::template type>::value;
// check whether given output matches in the filtered list
static_assert(value,
"cannot assign given match expression to "
"typed behavior, because at least one return "
"type does not match");
"cannot assign given match expression to "
"typed behavior, because at least one return "
"type does not match");
};
};
template <class T>
struct is_system_msg_handler : std::false_type { };
template <>
struct is_system_msg_handler<reacts_to<exit_msg>> : std::true_type { };
template <>
struct is_system_msg_handler<reacts_to<down_msg>> : std::true_type { };
// Tests whether the input list (IList) matches the
// signature list (SList) for a typed actor behavior
template <class SList, class IList>
struct valid_input {
// strip exit_msg and down_msg from input types,
// because they're always allowed
using adjusted_slist =
typename tl_filter_not<
SList,
is_system_msg_handler
>::type;
using adjusted_ilist =
typename tl_filter_not<
IList,
is_system_msg_handler
>::type;
// check for each element in IList that there's an element in SList that
// (1) has an identical input type list
// (2) has an identical output type list
// OR the output of the element in IList is skip_message_t
static_assert(detail::tl_is_distinct<IList>::value,
"given pattern is not distinct");
"given pattern is not distinct");
static constexpr bool value =
detail::tl_size<SList>::value == detail::tl_size<IList>::value &&
detail::tl_forall<IList,
valid_input_predicate<SList>::template inner>::value;
tl_size<adjusted_slist>::value == tl_size<adjusted_ilist>::value
&& tl_forall<
adjusted_ilist,
valid_input_predicate<adjusted_slist>::template inner
>::value;
};
// this function is called from typed_behavior<...>::set and its whole
......@@ -124,8 +150,8 @@ void static_check_typed_behavior_input() {
// input types ... however, it might lead to unexpected results
// and would cause a lot of not-so-straightforward code here
static_assert(is_valid,
"given pattern cannot be used to initialize "
"typed behavior (exact match needed)");
"given pattern cannot be used to initialize "
"typed behavior (exact match needed)");
}
} // namespace detail
......
......@@ -35,7 +35,6 @@ namespace {
struct my_request {
int a;
int b;
};
using server_type = typed_actor<replies_to<my_request>::with<bool>>;
......@@ -45,7 +44,11 @@ bool operator==(const my_request& lhs, const my_request& rhs) {
}
server_type::behavior_type typed_server1() {
return {[](const my_request& req) { return req.a == req.b; }};
return {
[](const my_request& req) {
return req.a == req.b;
}
};
}
server_type::behavior_type typed_server2(server_type::pointer) {
......@@ -246,12 +249,14 @@ int_actor::behavior_type int_fun() {
}
behavior foo(event_based_actor* self) {
return {on_arg_match >> [=](int i, int_actor server) {
return self->sync_send(server, i).then([=](int result)->int {
self->quit(exit_reason::normal);
return result;
});
}};
return {
on_arg_match >> [=](int i, int_actor server) {
return self->sync_send(server, i).then([=](int result) -> int {
self->quit(exit_reason::normal);
return result;
});
}
};
}
void test_sending_typed_actors() {
......@@ -262,6 +267,41 @@ void test_sending_typed_actors() {
self->send_exit(aut, exit_reason::user_shutdown);
}
int_actor::behavior_type int_fun2(int_actor::pointer self) {
self->trap_exit(true);
return {
[=](int i) {
self->monitor(self->last_sender());
return i * i;
},
[=](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
self->quit();
},
[=](const exit_msg& em) {
CAF_UNEXPECTED_MSG(self);
}
};
}
behavior foo2(event_based_actor* self) {
return {
[=](int i, int_actor server) {
return self->sync_send(server, i).then([=](int result) -> int {
self->quit(exit_reason::normal);
return result;
});
}
};
}
void test_sending_typed_actors_and_down_msg() {
scoped_actor self;
auto aut = spawn_typed(int_fun2);
self->send(spawn(foo2), 10, aut);
self->receive([](int i) { CAF_CHECK_EQUAL(i, 100); });
}
} // namespace <anonymous>
/******************************************************************************
......@@ -270,19 +310,15 @@ void test_sending_typed_actors() {
int main() {
CAF_TEST(test_typed_spawn);
// announce stuff
announce<get_state_msg>();
announce<int_actor>();
announce<my_request>(&my_request::a, &my_request::b);
// run test series with typed_server(1|2)
test_typed_spawn(spawn_typed(typed_server1));
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
test_typed_spawn(spawn_typed(typed_server2));
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
{
......@@ -290,24 +326,24 @@ int main() {
test_typed_spawn(spawn_typed<typed_server3>("hi there", self));
self->receive(on("hi there") >> CAF_CHECKPOINT_CB());
}
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
// run test series with event_testee
test_event_testee();
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
// run test series with string reverter
test_simple_string_reverter();
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
// run test series with sending of typed actors
test_sending_typed_actors();
await_all_actors_done();
CAF_CHECKPOINT();
// and again plus check whether typed actors can handle system messages
test_sending_typed_actors_and_down_msg();
await_all_actors_done();
CAF_CHECKPOINT();
// call it a day
return CAF_TEST_RESULT();
}
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