Unverified Commit 9e6d1e8b authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #961

Make messages visible to the actor profiler
parents e9cb0409 f50647d0
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#pragma once #pragma once
#include "caf/actor_clock.hpp"
#include "caf/config.hpp"
#include "caf/detail/build_config.hpp" #include "caf/detail/build_config.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -61,6 +63,33 @@ public: ...@@ -61,6 +63,33 @@ public:
virtual void after_processing(const local_actor& self, virtual void after_processing(const local_actor& self,
invoke_message_result result) invoke_message_result result)
= 0; = 0;
/// Called whenever an actor is about to send a message. Allows the profiler
/// to inject arbitrary meta data before putting the mailbox element into the
/// mailbox of the receiver.
/// @param self The current actor.
/// @param element The outgoing mailbox element.
/// @note The profiler gets a mutable reference to `element`, but it is only
/// supposed to inject meta data. Not to alter the message itself. Doing
/// so is an easy way to introduce bugs that are very hard to track
/// down.
/// @thread-safe
virtual void before_sending(const local_actor& self, mailbox_element& element)
= 0;
/// Analoguous to `before_sending`, but called whenever an actor is about to
/// call `actor_clock::schedule_message`.
/// @param self The current actor.
/// @param timeout Time point for the message delivery.
/// @param element The outgoing mailbox element.
/// @thread-safe
virtual void before_sending_scheduled(const local_actor& self,
caf::actor_clock::time_point timeout,
mailbox_element& element)
= 0;
// TODO: the instrumentation currently only works for actor-to-actor messages,
// but not when using group communication.
}; };
#ifdef CAF_ENABLE_ACTOR_PROFILER #ifdef CAF_ENABLE_ACTOR_PROFILER
...@@ -68,9 +97,15 @@ public: ...@@ -68,9 +97,15 @@ public:
self->system().profiler_before_processing(*self, msg) self->system().profiler_before_processing(*self, msg)
# define CAF_AFTER_PROCESSING(self, result) \ # define CAF_AFTER_PROCESSING(self, result) \
self->system().profiler_after_processing(*self, result) self->system().profiler_after_processing(*self, result)
# define CAF_BEFORE_SENDING(self, msg) \
self->system().profiler_before_sending(*self, msg)
# define CAF_BEFORE_SENDING_SCHEDULED(self, timeout, msg) \
self->system().profiler_before_sending_scheduled(*self, timeout, msg)
#else #else
# define CAF_BEFORE_PROCESSING(self, msg) CAF_VOID_STMT # define CAF_BEFORE_PROCESSING(self, msg) static_cast<void>(0)
# define CAF_AFTER_PROCESSING(self, result) CAF_VOID_STMT # define CAF_AFTER_PROCESSING(self, result) static_cast<void>(0)
# define CAF_BEFORE_SENDING(self, msg) static_cast<void>(0)
# define CAF_BEFORE_SENDING_SCHEDULED(self, timeout, msg) static_cast<void>(0)
#endif #endif
} // namespace caf } // namespace caf
...@@ -575,6 +575,19 @@ public: ...@@ -575,6 +575,19 @@ public:
profiler_->after_processing(self, result); profiler_->after_processing(self, result);
} }
void profiler_before_sending(const local_actor& self,
mailbox_element& element) {
if (profiler_)
profiler_->before_sending(self, element);
}
void profiler_before_sending_scheduled(const local_actor& self,
caf::actor_clock::time_point timeout,
mailbox_element& element) {
if (profiler_)
profiler_->before_sending_scheduled(self, timeout, element);
}
/// @endcond /// @endcond
private: private:
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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
#include <vector>
#include "caf/actor_cast.hpp"
#include "caf/actor_clock.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/actor_profiler.hpp"
#include "caf/fwd.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/message_id.hpp"
#include "caf/no_stages.hpp"
namespace caf {
namespace detail {
template <class Self, class Sender, class Handle, class... Ts>
void profiled_send(Self* self, Sender&& sender, const Handle& receiver,
message_id msg_id, std::vector<strong_actor_ptr> stages,
execution_unit* context, Ts&&... xs) {
if (receiver) {
auto element = make_mailbox_element(std::forward<Sender>(sender), msg_id,
std::move(stages),
std::forward<Ts>(xs)...);
CAF_BEFORE_SENDING(self, *element);
receiver->enqueue(std::move(element), context);
}
}
template <class Self, class Sender, class Handle, class... Ts>
void profiled_send(Self* self, Sender&& sender, const Handle& receiver,
actor_clock& clock, actor_clock::time_point timeout,
message_id msg_id, Ts&&... xs) {
if (receiver) {
auto element = make_mailbox_element(std::forward<Sender>(sender), msg_id,
no_stages, std::forward<Ts>(xs)...);
CAF_BEFORE_SENDING_SCHEDULED(self, timeout, *element);
clock.schedule_message(timeout, actor_cast<strong_actor_ptr>(receiver),
std::move(element));
}
}
} // namespace detail
} // namespace caf
...@@ -403,20 +403,8 @@ public: ...@@ -403,20 +403,8 @@ public:
detail::implicit_conversions_t< detail::implicit_conversions_t<
typename std::decay<Ts>::type>...>::delegated_type typename std::decay<Ts>::type>...>::delegated_type
delegate(const Handle& dest, Ts&&... xs) { delegate(const Handle& dest, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "nothing to delegate"); auto rp = make_response_promise();
using token = detail::type_list<typename detail::implicit_conversions< return rp.template delegate<P>(dest, std::forward<Ts>(xs)...);
typename std::decay<Ts>::type>::type...>;
static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid,
"receiver does not accept given message");
auto mid = current_element_->mid;
current_element_->mid = P == message_priority::high
? mid.with_high_priority()
: mid.with_normal_priority();
dest->enqueue(make_mailbox_element(std::move(current_element_->sender), mid,
std::move(current_element_->stages),
std::forward<Ts>(xs)...),
context());
return {};
} }
virtual void initialize(); virtual void initialize();
......
...@@ -21,15 +21,16 @@ ...@@ -21,15 +21,16 @@
#include <tuple> #include <tuple>
#include <chrono> #include <chrono>
#include "caf/fwd.hpp"
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/message.hpp" #include "caf/check_typed_input.hpp"
#include "caf/detail/profiled_send.hpp"
#include "caf/duration.hpp" #include "caf/duration.hpp"
#include "caf/fwd.hpp"
#include "caf/message.hpp"
#include "caf/message_id.hpp" #include "caf/message_id.hpp"
#include "caf/response_type.hpp"
#include "caf/response_handle.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/check_typed_input.hpp" #include "caf/response_handle.hpp"
#include "caf/response_type.hpp"
namespace caf { namespace caf {
namespace mixin { namespace mixin {
...@@ -77,17 +78,17 @@ public: ...@@ -77,17 +78,17 @@ public:
>::type...>; >::type...>;
static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid, static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid,
"receiver does not accept given message"); "receiver does not accept given message");
auto dptr = static_cast<Subtype*>(this); auto self = static_cast<Subtype*>(this);
auto req_id = dptr->new_request_id(P); auto req_id = self->new_request_id(P);
if (dest) { if (dest) {
dest->eq_impl(req_id, dptr->ctrl(), dptr->context(), detail::profiled_send(self, self->ctrl(), dest, req_id, {},
std::forward<Ts>(xs)...); self->context(), std::forward<Ts>(xs)...);
dptr->request_response_timeout(timeout, req_id); self->request_response_timeout(timeout, req_id);
} else { } else {
dptr->eq_impl(req_id.response_id(), dptr->ctrl(), dptr->context(), self->eq_impl(req_id.response_id(), self->ctrl(), self->context(),
make_error(sec::invalid_argument)); make_error(sec::invalid_argument));
} }
return {req_id.response_id(), dptr}; return {req_id.response_id(), self};
} }
/// Sends `{xs...}` as a synchronous message to `dest` with priority `mp`. /// Sends `{xs...}` as a synchronous message to `dest` with priority `mp`.
......
...@@ -18,14 +18,16 @@ ...@@ -18,14 +18,16 @@
#pragma once #pragma once
#include <tuple>
#include <chrono> #include <chrono>
#include <tuple>
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/actor_cast.hpp" #include "caf/actor_cast.hpp"
#include "caf/actor_clock.hpp" #include "caf/actor_clock.hpp"
#include "caf/actor_control_block.hpp" #include "caf/actor_control_block.hpp"
#include "caf/check_typed_input.hpp" #include "caf/check_typed_input.hpp"
#include "caf/detail/profiled_send.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/duration.hpp" #include "caf/duration.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
...@@ -35,6 +37,7 @@ ...@@ -35,6 +37,7 @@
#include "caf/response_type.hpp" #include "caf/response_type.hpp"
#include "caf/scheduler/abstract_coordinator.hpp" #include "caf/scheduler/abstract_coordinator.hpp"
#include "caf/send.hpp" #include "caf/send.hpp"
#include "caf/typed_actor_view_base.hpp"
namespace caf { namespace caf {
namespace mixin { namespace mixin {
...@@ -57,35 +60,44 @@ public: ...@@ -57,35 +60,44 @@ public:
// -- send function family --------------------------------------------------- // -- send function family ---------------------------------------------------
/// Sends `{xs...}` as an asynchronous message to `dest` with priority `mp`. /// Sends `{xs...}` as an asynchronous message to `dest` with priority `mp`.
template <message_priority P = message_priority::normal, template <message_priority P = message_priority::normal, class... Ts>
class Dest = actor, class... Ts> void send(const group& dest, Ts&&... xs) {
void send(const Dest& dest, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token; static_assert(!statically_typed<Subtype>(),
type_check(dest, args_token); "statically typed actors can only send() to other "
if (dest) "statically typed actors; use anon_send() when "
dest->eq_impl(make_message_id(P), dptr()->ctrl(), "communicating to groups");
dptr()->context(), std::forward<Ts>(xs)...); // TODO: consider whether it's feasible to track messages to groups
if (dest) {
auto self = dptr();
dest->eq_impl(make_message_id(P), self->ctrl(), self->context(),
std::forward<Ts>(xs)...);
}
} }
/// Sends `{xs...}` as an asynchronous message to `dest` with priority `mp`. /// Sends `{xs...}` as an asynchronous message to `dest` with priority `mp`.
template <message_priority P = message_priority::normal, class... Ts> template <message_priority P = message_priority::normal, class Dest,
void send(const strong_actor_ptr& dest, Ts&&... xs) { class... Ts>
using detail::type_list; detail::enable_if_t<!std::is_same<group, Dest>::value> send(const Dest& dest,
Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
static_assert(!statically_typed<Subtype>(), detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
"statically typed actors can only send() to other " type_check(dest, args_token);
"statically typed actors; use anon_send() or request() when " auto self = dptr();
"communicating with dynamically typed actors"); detail::profiled_send(self, self->ctrl(), dest, make_message_id(P), {},
if (dest) self->context(), std::forward<Ts>(xs)...);
dest->get()->eq_impl(make_message_id(P), dptr()->ctrl(),
dptr()->context(), std::forward<Ts>(xs)...);
} }
template <message_priority P = message_priority::normal, template <message_priority P = message_priority::normal, class Dest = actor,
class Dest = actor, class... Ts> class... Ts>
void anon_send(const Dest& dest, Ts&&... xs) { void anon_send(const Dest& dest, Ts&&... xs) {
caf::anon_send(dest, std::forward<Ts>(xs)...); static_assert(sizeof...(Ts) > 0, "no message to send");
using token = detail::type_list<detail::strip_and_convert_t<Ts>...>;
static_assert(response_type_unbox<signatures_of_t<Dest>, token>::valid,
"receiver does not accept given message");
auto self = dptr();
detail::profiled_send(self, self->ctrl(), dest, make_message_id(P), {},
self->context(), std::forward<Ts>(xs)...);
} }
/// Sends a message at given time point (or immediately if `timeout` has /// Sends a message at given time point (or immediately if `timeout` has
...@@ -98,13 +110,9 @@ public: ...@@ -98,13 +110,9 @@ public:
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token; detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
type_check(dest, args_token); type_check(dest, args_token);
if (dest) { auto self = dptr();
auto& clock = dptr()->system().clock(); detail::profiled_send(self, self->ctrl(), dest, self->system().clock(),
clock.schedule_message(timeout, actor_cast<strong_actor_ptr>(dest), timeout, make_message_id(P), std::forward<Ts>(xs)...);
make_mailbox_element(dptr()->ctrl(),
make_message_id(P), no_stages,
std::forward<Ts>(xs)...));
}
} }
/// Sends a message at given time point (or immediately if `timeout` has /// Sends a message at given time point (or immediately if `timeout` has
...@@ -115,9 +123,11 @@ public: ...@@ -115,9 +123,11 @@ public:
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
static_assert(!statically_typed<Subtype>(), static_assert(!statically_typed<Subtype>(),
"statically typed actors are not allowed to send to groups"); "statically typed actors are not allowed to send to groups");
// TODO: consider whether it's feasible to track messages to groups
if (dest) { if (dest) {
auto& clock = dptr()->system().clock(); auto self = dptr();
clock.schedule_message(timeout, dest, dptr()->ctrl(), auto& clock = self->system().clock();
clock.schedule_message(timeout, dest, self->ctrl(),
make_message(std::forward<Ts>(xs)...)); make_message(std::forward<Ts>(xs)...));
} }
} }
...@@ -131,14 +141,11 @@ public: ...@@ -131,14 +141,11 @@ public:
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token; detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
type_check(dest, args_token); type_check(dest, args_token);
if (dest) { auto self = dptr();
auto& clock = dptr()->system().clock(); auto& clock = self->system().clock();
auto timeout = clock.now() + rel_timeout; auto timeout = clock.now() + rel_timeout;
clock.schedule_message(timeout, actor_cast<strong_actor_ptr>(dest), detail::profiled_send(self, self->ctrl(), dest, clock, timeout,
make_mailbox_element(dptr()->ctrl(), make_message_id(P), std::forward<Ts>(xs)...);
make_message_id(P), no_stages,
std::forward<Ts>(xs)...));
}
} }
/// Sends a message after a relative timeout. /// Sends a message after a relative timeout.
...@@ -149,28 +156,73 @@ public: ...@@ -149,28 +156,73 @@ public:
static_assert(sizeof...(Ts) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
static_assert(!statically_typed<Subtype>(), static_assert(!statically_typed<Subtype>(),
"statically typed actors are not allowed to send to groups"); "statically typed actors are not allowed to send to groups");
// TODO: consider whether it's feasible to track messages to groups
if (dest) { if (dest) {
auto& clock = dptr()->system().clock(); auto self = dptr();
auto& clock = self->system().clock();
auto timeout = clock.now() + rtime; auto timeout = clock.now() + rtime;
clock.schedule_message(timeout, dest, dptr()->ctrl(), clock.schedule_message(timeout, dest, self->ctrl(),
make_message(std::forward<Ts>(xs)...)); make_message(std::forward<Ts>(xs)...));
} }
} }
template <message_priority P = message_priority::normal, class Dest = actor,
class... Ts>
void scheduled_anon_send(const Dest& dest, actor_clock::time_point timeout,
Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
type_check(dest, args_token);
auto self = dptr();
detail::profiled_send(self, self->ctrl(), dest, self->system().clock(),
timeout, make_message_id(P), std::forward<Ts>(xs)...);
}
template <message_priority P = message_priority::normal, class Dest = actor, template <message_priority P = message_priority::normal, class Dest = actor,
class Rep = int, class Period = std::ratio<1>, class... Ts> class Rep = int, class Period = std::ratio<1>, class... Ts>
void delayed_anon_send(const Dest& dest, void delayed_anon_send(const Dest& dest,
std::chrono::duration<Rep, Period> rtime, Ts&&... xs) { std::chrono::duration<Rep, Period> rel_timeout,
caf::delayed_anon_send<P>(dest, rtime, std::forward<Ts>(xs)...); Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
type_check(dest, args_token);
auto self = dptr();
auto& clock = self->system().clock();
auto timeout = clock.now() + rel_timeout;
detail::profiled_send(self, self->ctrl(), dest, clock, timeout,
make_message_id(P), std::forward<Ts>(xs)...);
} }
template <class Rep = int, class Period = std::ratio<1>, class... Ts> template <class Rep = int, class Period = std::ratio<1>, class... Ts>
void delayed_anon_send(const group& dest, void delayed_anon_send(const group& dest,
std::chrono::duration<Rep, Period> rtime, Ts&&... xs) { std::chrono::duration<Rep, Period> rtime, Ts&&... xs) {
caf::delayed_anon_send(dest, rtime, std::forward<Ts>(xs)...); delayed_anon_send_impl(dest, rtime, std::forward<Ts>(xs)...);
} }
private: private:
template <class Dest, class... Ts>
void scheduled_send_impl(message_id mid, const Dest& dest, actor_clock& clock,
actor_clock::time_point timeout, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
type_check(dest, args_token);
auto self = dptr();
detail::profiled_send(self, self->ctrl(), dest, clock, timeout, mid,
std::forward<Ts>(xs)...);
}
template <class Dest, class... Ts>
void scheduled_anon_send_impl(message_id mid, const Dest& dest,
actor_clock& clock,
actor_clock::time_point timeout, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "no message to send");
detail::type_list<detail::strip_and_convert_t<Ts>...> args_token;
type_check(dest, args_token);
auto self = dptr();
detail::profiled_send(self, nullptr, dest, clock, timeout, mid,
std::forward<Ts>(xs)...);
}
template <class Dest, class ArgTypes> template <class Dest, class ArgTypes>
static void type_check(const Dest&, ArgTypes) { static void type_check(const Dest&, ArgTypes) {
static_assert(!statically_typed<Subtype>() || statically_typed<Dest>(), static_assert(!statically_typed<Subtype>() || statically_typed<Dest>(),
...@@ -186,9 +238,17 @@ private: ...@@ -186,9 +238,17 @@ private:
"this actor does not accept the response message"); "this actor does not accept the response message");
} }
Subtype* dptr() { template <class T = Base>
detail::enable_if_t<std::is_base_of<abstract_actor, T>::value, Subtype*>
dptr() {
return static_cast<Subtype*>(this); return static_cast<Subtype*>(this);
} }
template <class T = Subtype, class = detail::enable_if_t<std::is_base_of<
typed_actor_view_base, T>::value>>
typename T::pointer dptr() {
return static_cast<Subtype*>(this)->internal_ptr();
}
}; };
} // namespace mixin } // namespace mixin
......
...@@ -21,11 +21,12 @@ ...@@ -21,11 +21,12 @@
#include <vector> #include <vector>
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/message.hpp"
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/actor_cast.hpp"
#include "caf/check_typed_input.hpp"
#include "caf/message.hpp"
#include "caf/message_id.hpp" #include "caf/message_id.hpp"
#include "caf/response_type.hpp" #include "caf/response_type.hpp"
#include "caf/check_typed_input.hpp"
namespace caf { namespace caf {
...@@ -56,15 +57,15 @@ public: ...@@ -56,15 +57,15 @@ public:
detail::enable_if_t<((sizeof...(Ts) > 0) detail::enable_if_t<((sizeof...(Ts) > 0)
|| (!std::is_convertible<T, error>::value || (!std::is_convertible<T, error>::value
&& !std::is_same<detail::decay_t<T>, unit_t>::value)) && !std::is_same<detail::decay_t<T>, unit_t>::value))
&& !detail::is_expected<detail::decay_t<T>>::value> && !detail::is_expected<detail::decay_t<T>>::value>
deliver(T&& x, Ts&&... xs) { deliver(T&& x, Ts&&... xs) {
using ts = detail::type_list<detail::decay_t<T>, detail::decay_t<Ts>...>; using ts = detail::type_list<detail::decay_t<T>, detail::decay_t<Ts>...>;
static_assert(!detail::tl_exists<ts, detail::is_result>::value, static_assert(!detail::tl_exists<ts, detail::is_result>::value,
"it is not possible to deliver objects of type result<T>"); "it is not possible to deliver objects of type result<T>");
static_assert(!detail::tl_exists<ts, detail::is_expected>::value, static_assert(!detail::tl_exists<ts, detail::is_expected>::value,
"mixing expected<T> with regular values is not supported"); "mixing expected<T> with regular values is not supported");
return deliver_impl(make_message(std::forward<T>(x), return deliver_impl(
std::forward<Ts>(xs)...)); make_message(std::forward<T>(x), std::forward<Ts>(xs)...));
} }
template <class T> template <class T>
...@@ -75,28 +76,22 @@ public: ...@@ -75,28 +76,22 @@ public:
} }
/// Satisfies the promise by delegating to another actor. /// Satisfies the promise by delegating to another actor.
template <message_priority P = message_priority::normal, template <message_priority P = message_priority::normal, class Handle = actor,
class Handle = actor, class... Ts> class... Ts>
typename response_type< typename response_type<typename Handle::signatures,
typename Handle::signatures, detail::implicit_conversions_t<
detail::implicit_conversions_t<typename std::decay<Ts>::type>... typename std::decay<Ts>::type>...>::delegated_type
>::delegated_type
delegate(const Handle& dest, Ts&&... xs) { delegate(const Handle& dest, Ts&&... xs) {
static_assert(sizeof...(Ts) > 0, "nothing to delegate"); static_assert(sizeof...(Ts) > 0, "nothing to delegate");
using token = using token = detail::type_list<typename detail::implicit_conversions<
detail::type_list< typename std::decay<Ts>::type>::type...>;
typename detail::implicit_conversions<
typename std::decay<Ts>::type
>::type...>;
static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid, static_assert(response_type_unbox<signatures_of_t<Handle>, token>::valid,
"receiver does not accept given message"); "receiver does not accept given message");
if (dest) { // TODO: use `if constexpr` when switching to C++17
auto mid = P == message_priority::high ? id_.with_high_priority() : id_; if (P == message_priority::high)
dest->enqueue(make_mailbox_element(std::move(source_), mid, id_ = id_.with_high_priority();
std::move(stages_), delegate_impl(actor_cast<abstract_actor*>(dest),
std::forward<Ts>(xs)...), make_message(std::forward<Ts>(xs)...));
context());
}
return {}; return {};
} }
...@@ -111,36 +106,41 @@ public: ...@@ -111,36 +106,41 @@ public:
bool async() const; bool async() const;
/// Queries whether this promise is a valid promise that is not satisfied yet. /// Queries whether this promise is a valid promise that is not satisfied yet.
inline bool pending() const { bool pending() const {
return source_ != nullptr || !stages_.empty(); return source_ != nullptr || !stages_.empty();
} }
/// Returns the source of the corresponding request. /// Returns the source of the corresponding request.
inline const strong_actor_ptr& source() const { const strong_actor_ptr& source() const {
return source_; return source_;
} }
/// Returns the remaining stages for the corresponding request. /// Returns the remaining stages for the corresponding request.
inline const forwarding_stack& stages() const { const forwarding_stack& stages() const {
return stages_; return stages_;
} }
/// Returns the actor that will receive the response, i.e., /// Returns the actor that will receive the response, i.e.,
/// `stages().front()` if `!stages().empty()` or `source()` otherwise. /// `stages().front()` if `!stages().empty()` or `source()` otherwise.
inline strong_actor_ptr next() const { strong_actor_ptr next() const {
return stages_.empty() ? source_ : stages_.front(); return stages_.empty() ? source_ : stages_.front();
} }
/// Returns the message ID of the corresponding request. /// Returns the message ID of the corresponding request.
inline message_id id() const { message_id id() const {
return id_; return id_;
} }
private: private:
/// Returns a downcasted version of `self_`.
local_actor* self_dptr() const;
execution_unit* context(); execution_unit* context();
void deliver_impl(message msg); void deliver_impl(message msg);
void delegate_impl(abstract_actor* receiver, message msg);
strong_actor_ptr self_; strong_actor_ptr self_;
strong_actor_ptr source_; strong_actor_ptr source_;
forwarding_stack stages_; forwarding_stack stages_;
......
...@@ -18,15 +18,13 @@ ...@@ -18,15 +18,13 @@
#pragma once #pragma once
#include "caf/scheduled_actor.hpp"
#include "caf/mixin/sender.hpp"
#include "caf/mixin/requester.hpp" #include "caf/mixin/requester.hpp"
#include "caf/mixin/sender.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/typed_actor_view_base.hpp"
namespace caf { namespace caf {
struct typed_actor_view_base { };
template <class... Sigs> template <class... Sigs>
class typed_actor_view : public extend<typed_actor_view_base, class typed_actor_view : public extend<typed_actor_view_base,
typed_actor_view<Sigs...>>::template typed_actor_view<Sigs...>>::template
...@@ -35,6 +33,8 @@ public: ...@@ -35,6 +33,8 @@ public:
/// Stores the template parameter pack. /// Stores the template parameter pack.
using signatures = detail::type_list<Sigs...>; using signatures = detail::type_list<Sigs...>;
using pointer = scheduled_actor*;
typed_actor_view(scheduled_actor* ptr) : self_(ptr) { typed_actor_view(scheduled_actor* ptr) : self_(ptr) {
// nop // nop
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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
namespace caf {
/// Tag type for @ref typed_actor_view.
struct typed_actor_view_base {};
} // namespace caf
...@@ -16,13 +16,15 @@ ...@@ -16,13 +16,15 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include <utility>
#include <algorithm> #include <algorithm>
#include <utility>
#include "caf/response_promise.hpp" #include "caf/response_promise.hpp"
#include "caf/logger.hpp" #include "caf/detail/profiled_send.hpp"
#include "caf/local_actor.hpp" #include "caf/local_actor.hpp"
#include "caf/logger.hpp"
#include "caf/no_stages.hpp"
namespace caf { namespace caf {
...@@ -37,21 +39,20 @@ response_promise::response_promise(none_t) : response_promise() { ...@@ -37,21 +39,20 @@ response_promise::response_promise(none_t) : response_promise() {
response_promise::response_promise(strong_actor_ptr self, response_promise::response_promise(strong_actor_ptr self,
strong_actor_ptr source, strong_actor_ptr source,
forwarding_stack stages, message_id mid) forwarding_stack stages, message_id mid)
: self_(std::move(self)), : id_(mid) {
source_(std::move(source)), CAF_ASSERT(self != nullptr);
stages_(std::move(stages)),
id_(mid) {
// Form an invalid request promise when initialized from a response ID, since // Form an invalid request promise when initialized from a response ID, since
// we always drop messages in this case. // we always drop messages in this case.
if (mid.is_response()) { if (!mid.is_response()) {
source_ = nullptr; self_.swap(self);
stages_.clear(); source_.swap(source);
stages_.swap(stages);
} }
} }
response_promise::response_promise(strong_actor_ptr self, mailbox_element& src) response_promise::response_promise(strong_actor_ptr self, mailbox_element& src)
: response_promise(std::move(self), std::move(src.sender), : response_promise(std::move(self), std::move(src.sender),
std::move(src.stages), src.mid) { std::move(src.stages), src.mid) {
// nop // nop
} }
...@@ -67,32 +68,60 @@ bool response_promise::async() const { ...@@ -67,32 +68,60 @@ bool response_promise::async() const {
return id_.is_async(); return id_.is_async();
} }
local_actor* response_promise::self_dptr() const {
// TODO: We require that self_ was constructed by using a local_actor*. The
// type erasure performed by strong_actor_ptr hides that fact. We
// probably should use a different pointer type such as
// intrusive_ptr<local_actor>. However, that would mean we would have to
// include local_actor.hpp in response_promise.hpp or provide overloads
// for intrusive_ptr_add_ref and intrusive_ptr_release.
auto self_baseptr = actor_cast<abstract_actor*>(self_);
return static_cast<local_actor*>(self_baseptr);
}
execution_unit* response_promise::context() { execution_unit* response_promise::context() {
return self_ == nullptr return self_ == nullptr ? nullptr : self_dptr()->context();
? nullptr
: static_cast<local_actor*>(actor_cast<abstract_actor*>(self_))
->context();
} }
void response_promise::deliver_impl(message msg) { void response_promise::deliver_impl(message msg) {
CAF_LOG_TRACE(CAF_ARG(msg)); CAF_LOG_TRACE(CAF_ARG(msg));
if (self_ == nullptr) {
CAF_LOG_DEBUG("drop response: invalid promise");
return;
}
auto dptr = self_dptr();
if (!stages_.empty()) { if (!stages_.empty()) {
auto next = std::move(stages_.back()); auto next = std::move(stages_.back());
stages_.pop_back(); stages_.pop_back();
next->enqueue(make_mailbox_element(std::move(source_), id_, detail::profiled_send(dptr, std::move(source_), next, id_,
std::move(stages_), std::move(msg)), std::move(stages_), dptr->context(), std::move(msg));
context()); self_.reset();
return; return;
} }
if (source_) { if (source_) {
source_->enqueue(std::move(self_), id_.response_id(), detail::profiled_send(dptr, self_, source_, id_.response_id(), no_stages,
std::move(msg), context()); dptr->context(), std::move(msg));
self_.reset();
source_.reset(); source_.reset();
return; return;
} }
CAF_LOG_INFO_IF(self_ != nullptr, "response promise already satisfied"); CAF_LOG_WARNING("malformed response promise: self != nullptr && !pending()");
CAF_LOG_INFO_IF(self_ == nullptr, "invalid response promise"); }
void response_promise::delegate_impl(abstract_actor* receiver, message msg) {
CAF_LOG_TRACE(CAF_ARG(msg));
if (receiver == nullptr) {
CAF_LOG_DEBUG("drop response: invalid delegation target");
return;
}
if (self_ == nullptr) {
CAF_LOG_DEBUG("drop response: invalid promise");
return;
}
auto dptr = self_dptr();
detail::profiled_send(dptr, std::move(source_), receiver, id_,
std::move(stages_), dptr->context(), std::move(msg));
self_.reset();
} }
} // namespace caf } // namespace caf
...@@ -33,7 +33,7 @@ namespace { ...@@ -33,7 +33,7 @@ namespace {
using string_list = std::vector<std::string>; using string_list = std::vector<std::string>;
struct recorder : actor_profiler { struct recorder : actor_profiler {
void add_actor(const local_actor& self, const local_actor* parent) { void add_actor(const local_actor& self, const local_actor* parent) override {
log.emplace_back("new: "); log.emplace_back("new: ");
auto& str = log.back(); auto& str = log.back();
str += self.name(); str += self.name();
...@@ -43,20 +43,21 @@ struct recorder : actor_profiler { ...@@ -43,20 +43,21 @@ struct recorder : actor_profiler {
} }
} }
void remove_actor(const local_actor& self) { void remove_actor(const local_actor& self) override {
log.emplace_back("delete: "); log.emplace_back("delete: ");
log.back() += self.name(); log.back() += self.name();
} }
void before_processing(const local_actor& self, void before_processing(const local_actor& self,
const mailbox_element& element) { const mailbox_element& element) override {
log.emplace_back(self.name()); log.emplace_back(self.name());
auto& str = log.back(); auto& str = log.back();
str += " got: "; str += " got: ";
str += to_string(element.content()); str += to_string(element.content());
} }
void after_processing(const local_actor& self, invoke_message_result result) { void after_processing(const local_actor& self,
invoke_message_result result) override {
log.emplace_back(self.name()); log.emplace_back(self.name());
auto& str = log.back(); auto& str = log.back();
str += " "; str += " ";
...@@ -64,6 +65,23 @@ struct recorder : actor_profiler { ...@@ -64,6 +65,23 @@ struct recorder : actor_profiler {
str += " the message"; str += " the message";
} }
void before_sending(const local_actor& self,
mailbox_element& element) override {
log.emplace_back(self.name());
auto& str = log.back();
str += " sends: ";
str += to_string(element.content());
}
void before_sending_scheduled(const local_actor& self,
actor_clock::time_point,
mailbox_element& element) override {
log.emplace_back(self.name());
auto& str = log.back();
str += " sends (scheduled): ";
str += to_string(element.content());
}
string_list log; string_list log;
}; };
...@@ -92,19 +110,22 @@ struct fixture { ...@@ -92,19 +110,22 @@ struct fixture {
scheduler_type& sched; scheduler_type& sched;
}; };
struct foo_state { # define NAMED_ACTOR_STATE(type) \
const char* name = "foo"; struct type##_state { \
}; const char* name = #type; \
}
struct bar_state { NAMED_ACTOR_STATE(bar);
const char* name = "bar"; NAMED_ACTOR_STATE(client);
}; NAMED_ACTOR_STATE(foo);
NAMED_ACTOR_STATE(server);
NAMED_ACTOR_STATE(worker);
} // namespace } // namespace
CAF_TEST_FIXTURE_SCOPE(actor_profiler_tests, fixture) CAF_TEST_FIXTURE_SCOPE(actor_profiler_tests, fixture)
CAF_TEST(record actor construction) { CAF_TEST(profilers record actor construction) {
CAF_MESSAGE("fully initialize CAF, ignore system-internal actors"); CAF_MESSAGE("fully initialize CAF, ignore system-internal actors");
run(); run();
rec.log.clear(); rec.log.clear();
...@@ -123,7 +144,7 @@ CAF_TEST(record actor construction) { ...@@ -123,7 +144,7 @@ CAF_TEST(record actor construction) {
rec.log); rec.log);
} }
CAF_TEST(record actor messaging) { CAF_TEST(profilers record asynchronous messaging) {
CAF_MESSAGE("fully initialize CAF, ignore system-internal actors"); CAF_MESSAGE("fully initialize CAF, ignore system-internal actors");
run(); run();
rec.log.clear(); rec.log.clear();
...@@ -148,7 +169,9 @@ CAF_TEST(record actor messaging) { ...@@ -148,7 +169,9 @@ CAF_TEST(record actor messaging) {
CAF_CHECK_EQUAL(string_list({ CAF_CHECK_EQUAL(string_list({
"new: foo", "new: foo",
"new: bar, parent: foo", "new: bar, parent: foo",
"foo sends: (\"hello bar\")",
"bar got: (\"hello bar\")", "bar got: (\"hello bar\")",
"bar sends: (\"hello foo\")",
"bar consumed the message", "bar consumed the message",
"foo got: (\"hello foo\")", "foo got: (\"hello foo\")",
"delete: bar", "delete: bar",
...@@ -158,6 +181,51 @@ CAF_TEST(record actor messaging) { ...@@ -158,6 +181,51 @@ CAF_TEST(record actor messaging) {
rec.log); rec.log);
} }
CAF_TEST(profilers record request / response messaging) {
CAF_MESSAGE("fully initialize CAF, ignore system-internal actors");
run();
rec.log.clear();
CAF_MESSAGE("spawn a client and a server with one worker");
auto worker = [](stateful_actor<worker_state>*) -> behavior {
return {
[](int x, int y) { return x + y; },
};
};
auto server = [](stateful_actor<server_state>* self, actor work) -> behavior {
return {
[=](int x, int y) { return self->delegate(work, x, y); },
};
};
auto client = [](stateful_actor<client_state>* self, actor serv) {
self->request(serv, infinite, 19, 23).then([](int result) {
CAF_CHECK_EQUAL(result, 42);
});
};
sys.spawn(client, sys.spawn(server, sys.spawn(worker)));
run();
for (const auto& line : rec.log) {
CAF_MESSAGE(line);
}
CAF_CHECK_EQUAL(string_list({
"new: worker",
"new: server",
"new: client",
"client sends: (19, 23)",
"server got: (19, 23)",
"server sends: (19, 23)",
"server consumed the message",
"delete: server",
"worker got: (19, 23)",
"worker sends: (42)",
"worker consumed the message",
"client got: (42)",
"client consumed the message",
"delete: worker",
"delete: client",
}),
rec.log);
}
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
#endif // CAF_ENABLE_ACTOR_PROFILER #endif // CAF_ENABLE_ACTOR_PROFILER
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