Commit f4ddeb2b authored by Dominik Charousset's avatar Dominik Charousset

Fix `then` and `await`, close #143

Synchronous response message are handled properly and patterns are no longer
extended automatically. The extension caused CAF to assume `on(any_vals,
arg_match)` before any functor given to `then` or `await`. This feature is
neither documented nor is it the expected behavior from a users point of view.
This could potentially break code, but the broken code shouldn't be valid in
the first place...
parent 1b744245
......@@ -29,6 +29,7 @@ set (LIBCAF_CORE_SRCS
src/attachable.cpp
src/behavior.cpp
src/behavior_stack.cpp
src/behavior_impl.cpp
src/binary_deserializer.cpp
src/binary_serializer.cpp
src/blocking_actor.cpp
......
......@@ -60,20 +60,19 @@ struct is_message_id_wrapper {
template <class U>
static char (&test(...))[2];
static constexpr bool value = sizeof(test<T>(0)) == 1;
};
template <class T>
struct optional_message_visitor_enable_tpl {
using type = typename std::remove_const<T>::type;
static constexpr bool value =
!detail::is_one_of<type,
none_t,
unit_t,
skip_message_t,
optional<skip_message_t>>::value &&
!is_message_id_wrapper<T>::value;
!detail::is_one_of<
typename std::remove_const<T>::type,
none_t,
unit_t,
skip_message_t,
optional<skip_message_t>
>::value
&& !is_message_id_wrapper<T>::value;
};
struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
......@@ -115,13 +114,13 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
}
template <class... Ts>
inline bhvr_invoke_result operator()(std::tuple<Ts...>& value) const {
bhvr_invoke_result operator()(std::tuple<Ts...>& value) const {
return detail::apply_args(*this, detail::get_indices(value), value);
}
// <backward_compatibility version="0.9">
template <class... Ts>
inline bhvr_invoke_result operator()(cow_tuple<Ts...>& value) const {
bhvr_invoke_result operator()(cow_tuple<Ts...>& value) const {
return static_cast<message>(std::move(value));
}
// </backward_compatibility>
......@@ -132,89 +131,67 @@ template <class... Ts>
struct has_skip_message {
static constexpr bool value =
detail::disjunction<std::is_same<Ts, skip_message_t>::value...>::value;
};
class behavior_impl : public ref_counted {
public:
using pointer = intrusive_ptr<behavior_impl>;
~behavior_impl();
behavior_impl() = default;
inline behavior_impl(duration tout) : m_timeout(tout) {}
behavior_impl(duration tout);
virtual bhvr_invoke_result invoke(message&) = 0;
virtual bhvr_invoke_result invoke(const message&) = 0;
inline bhvr_invoke_result invoke(message&& arg) {
message tmp(std::move(arg));
return invoke(tmp);
}
virtual void handle_timeout();
inline const duration& timeout() const { return m_timeout; }
using pointer = intrusive_ptr<behavior_impl>;
inline const duration& timeout() const {
return m_timeout;
}
virtual pointer copy(const generic_timeout_definition& tdef) const = 0;
inline pointer or_else(const pointer& other) {
CAF_REQUIRE(other != nullptr);
struct combinator : behavior_impl {
pointer first;
pointer second;
bhvr_invoke_result invoke(message& arg) {
auto res = first->invoke(arg);
if (!res) return second->invoke(arg);
return res;
}
bhvr_invoke_result invoke(const message& arg) {
auto res = first->invoke(arg);
if (!res) return second->invoke(arg);
return res;
}
void handle_timeout() {
// the second behavior overrides the timeout handling of
// first behavior
return second->handle_timeout();
}
pointer copy(const generic_timeout_definition& tdef) const {
return new combinator(first, second->copy(tdef));
}
combinator(const pointer& p0, const pointer& p1)
: behavior_impl(p1->timeout()), first(p0), second(p1) {}
};
return new combinator(this, other);
}
pointer or_else(const pointer& other);
private:
duration m_timeout;
};
struct dummy_match_expr {
inline variant<none_t> invoke(const message&) const { return none; }
inline bool can_invoke(const message&) const { return false; }
inline variant<none_t> operator()(const message&) const { return none; }
inline variant<none_t> invoke(const message&) const {
return none;
}
inline bool can_invoke(const message&) const {
return false;
}
inline variant<none_t> operator()(const message&) const {
return none;
}
};
template <class MatchExpr, typename F>
class default_behavior_impl : public behavior_impl {
using super = behavior_impl;
public:
template <class Expr>
default_behavior_impl(Expr&& expr, const timeout_definition<F>& d)
: super(d.timeout)
: behavior_impl(d.timeout)
, m_expr(std::forward<Expr>(expr))
, m_fun(d.handler) {}
template <class Expr>
default_behavior_impl(Expr&& expr, duration tout, F f)
: super(tout), m_expr(std::forward<Expr>(expr)), m_fun(f) {}
: behavior_impl(tout),
m_expr(std::forward<Expr>(expr)),
m_fun(f) {
// nop
}
bhvr_invoke_result invoke(message& tup) {
auto res = m_expr(tup);
......@@ -230,17 +207,17 @@ class default_behavior_impl : public behavior_impl {
typename behavior_impl::pointer
copy(const generic_timeout_definition& tdef) const {
return new default_behavior_impl<MatchExpr, std::function<void()>>(
m_expr, tdef);
return new default_behavior_impl<MatchExpr, std::function<void()>>(m_expr,
tdef);
}
void handle_timeout() { m_fun(); }
void handle_timeout() {
m_fun();
}
private:
MatchExpr m_expr;
F m_fun;
};
template <class MatchExpr, typename F>
......@@ -250,12 +227,13 @@ new_default_behavior(const MatchExpr& mexpr, duration d, F f) {
}
template <class F>
default_behavior_impl<dummy_match_expr, F>* new_default_behavior(duration d,
F f) {
return new default_behavior_impl<dummy_match_expr, F>(dummy_match_expr{}, d,
f);
behavior_impl* new_default_behavior(duration d, F f) {
dummy_match_expr nop;
return new default_behavior_impl<dummy_match_expr, F>(nop, d, std::move(f));
}
behavior_impl* new_default_behavior(duration d, std::function<void()> fun);
using behavior_impl_ptr = intrusive_ptr<behavior_impl>;
// implemented in message_handler.cpp
......
......@@ -33,7 +33,6 @@
#include "caf/mixin/behavior_stack_based.hpp"
#include "caf/detail/logging.hpp"
#include "caf/detail/response_handle_util.hpp"
namespace caf {
......
......@@ -25,12 +25,11 @@
#include "caf/message_id.hpp"
#include "caf/typed_behavior.hpp"
#include "caf/continue_helper.hpp"
#include "caf/system_messages.hpp"
#include "caf/typed_continue_helper.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/typed_actor_util.hpp"
#include "caf/detail/response_handle_util.hpp"
namespace caf {
......@@ -69,27 +68,22 @@ class response_handle<Self, message, nonblocking_response_handle_tag> {
// nop
}
continue_helper then(behavior bhvr) const {
template <class T, class... Ts>
continue_helper then(T arg, Ts&&... args) const {
auto selfptr = m_self;
behavior bhvr{
std::move(arg),
std::forward<Ts>(args)...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
m_self->bhvr_stack().push_back(std::move(bhvr), m_mid);
auto ptr = m_self;
return {m_mid, [ptr](message_id mid) { return ptr->sync_handler(mid); }};
}
template <class... Cs, class... Ts>
continue_helper then(const match_expr<Cs...>& arg,
const Ts&... args) const {
return then(behavior{arg, args...});
}
template <class... Fs>
typename std::enable_if<
detail::all_callable<Fs...>::value,
continue_helper
>::type
then(Fs... fs) const {
return then(detail::fs2bhvr(m_self, fs...));
}
private:
message_id m_mid;
Self* m_self;
......@@ -121,9 +115,17 @@ class response_handle<Self, detail::type_list<Ts...>,
>::type>
then(F fun) {
detail::assert_types<detail::type_list<Ts...>, F>();
m_self->bhvr_stack().push_back(behavior{on_arg_match >> fun}, m_mid);
auto ptr = m_self;
return {m_mid, [ptr](message_id mid) { return ptr->sync_handler(mid); }};
auto selfptr = m_self;
behavior tmp{
fun,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
m_self->bhvr_stack().push_back(std::move(tmp), m_mid);
auto get = [selfptr](message_id mid) { return selfptr->sync_handler(mid); };
return {m_mid, get};
}
private:
......@@ -145,25 +147,22 @@ class response_handle<Self, message, blocking_response_handle_tag> {
// nop
}
void await(behavior& pfun) {
m_self->dequeue_response(pfun, m_mid);
}
void await(behavior&& pfun) {
behavior tmp{std::move(pfun)};
await(tmp);
}
template <class... Cs, class... Ts>
void await(const match_expr<Cs...>& arg, const Ts&... args) {
behavior bhvr{arg, args...};
await(bhvr);
void await(behavior& bhvr) {
m_self->dequeue_response(bhvr, m_mid);
}
template <class... Fs>
typename std::enable_if<detail::all_callable<Fs...>::value>::type
await(Fs... fs) {
await(detail::fs2bhvr(m_self, fs...));
template <class T, class... Ts>
void await(T arg, Ts&&... args) const {
auto selfptr = m_self;
behavior bhvr{
std::move(arg),
std::forward<Ts>(args)...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
m_self->dequeue_response(bhvr, m_mid);
}
private:
......@@ -197,15 +196,22 @@ class response_handle<Self, detail::type_list<Ts...>,
>::type;
static constexpr size_t fun_args = detail::tl_size<arg_types>::value;
static_assert(fun_args <= detail::tl_size<result_types>::value,
"functor takes too much arguments");
"functor takes too much arguments");
using recv_types =
typename detail::tl_right<
result_types,
fun_args
>::type;
static_assert(std::is_same<arg_types, recv_types>::value,
"wrong functor signature");
behavior tmp = detail::fs2bhvr(m_self, fun);
"wrong functor signature");
auto selfptr = m_self;
behavior tmp{
fun,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
m_self->dequeue_response(tmp, m_mid);
}
......
......@@ -17,8 +17,8 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef TIMEOUT_DEFINITION_HPP
#define TIMEOUT_DEFINITION_HPP
#ifndef CAF_TIMEOUT_DEFINITION_HPP
#define CAF_TIMEOUT_DEFINITION_HPP
#include <functional>
......@@ -27,20 +27,25 @@
namespace caf {
namespace detail {
class behavior_impl;
}
behavior_impl* new_default_behavior(duration d, std::function<void()> fun);
} // namespace detail
template <class F>
struct timeout_definition {
static constexpr bool may_have_timeout = true;
duration timeout;
F handler;
detail::behavior_impl* as_behavior_impl() const;
detail::behavior_impl* as_behavior_impl() const {
return detail::new_default_behavior(timeout, handler);
}
};
using generic_timeout_definition = timeout_definition<std::function<void()>>;
} // namespace caf
#endif // TIMEOUT_DEFINITION_HPP
#endif // CAF_TIMEOUT_DEFINITION_HPP
......@@ -17,33 +17,71 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_RESPONSE_FUTURE_DETAIL_HPP
#define CAF_DETAIL_RESPONSE_FUTURE_DETAIL_HPP
#include "caf/detail/behavior_impl.hpp"
#include "caf/on.hpp"
#include "caf/skip_message.hpp"
#include "caf/system_messages.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/message_handler.hpp"
namespace caf {
namespace detail {
template <class Actor, class... Fs>
behavior fs2bhvr(Actor* self, Fs... fs) {
auto handle_sync_timeout = [self]() -> skip_message_t {
self->handle_sync_timeout();
return {};
};
return behavior{
on<sync_timeout_msg>() >> handle_sync_timeout,
on<unit_t>() >> skip_message,
on<sync_exited_msg>() >> skip_message,
(on(any_vals, arg_match) >> std::move(fs))...
};
namespace {
class combinator : public behavior_impl {
public:
bhvr_invoke_result invoke(message& arg) {
auto res = first->invoke(arg);
if (!res) return second->invoke(arg);
return res;
}
bhvr_invoke_result invoke(const message& arg) {
auto res = first->invoke(arg);
if (!res) return second->invoke(arg);
return res;
}
void handle_timeout() {
// the second behavior overrides the timeout handling of
// first behavior
return second->handle_timeout();
}
pointer copy(const generic_timeout_definition& tdef) const {
return new combinator(first, second->copy(tdef));
}
combinator(const pointer& p0, const pointer& p1)
: behavior_impl(p1->timeout()),
first(p0),
second(p1) {
// nop
}
private:
pointer first;
pointer second;
};
} // namespace <anonymous>
behavior_impl::~behavior_impl() {
// nop
}
behavior_impl::behavior_impl(duration tout) : m_timeout(tout) {
// nop
}
behavior_impl::pointer behavior_impl::or_else(const pointer& other) {
CAF_REQUIRE(other != nullptr);
return new combinator(this, other);
}
behavior_impl* new_default_behavior(duration d, std::function<void()> fun) {
using impl = default_behavior_impl<dummy_match_expr, std::function<void()>>;
dummy_match_expr nop;
return new impl(nop, d, std::move(fun));
}
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_RESPONSE_FUTURE_DETAIL_HPP
This diff is collapsed.
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