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