Unverified Commit fddc1747 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #946

Add experimental API for actor profiling
parents 8607ad2f 41d3df49
...@@ -13,6 +13,7 @@ defaultReleaseBuildFlags = [ ...@@ -13,6 +13,7 @@ defaultReleaseBuildFlags = [
defaultDebugBuildFlags = defaultReleaseBuildFlags + [ defaultDebugBuildFlags = defaultReleaseBuildFlags + [
'CAF_ENABLE_ADDRESS_SANITIZER:BOOL=yes', 'CAF_ENABLE_ADDRESS_SANITIZER:BOOL=yes',
'CAF_LOG_LEVEL:STRING=TRACE', 'CAF_LOG_LEVEL:STRING=TRACE',
'CAF_ENABLE_ACTOR_PROFILER:BOOL=yes',
] ]
// Configures the behavior of our stages. // Configures the behavior of our stages.
......
...@@ -31,3 +31,4 @@ ...@@ -31,3 +31,4 @@
#cmakedefine CAF_NO_EXCEPTIONS #cmakedefine CAF_NO_EXCEPTIONS
#cmakedefine CAF_ENABLE_ACTOR_PROFILER
...@@ -81,6 +81,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]... ...@@ -81,6 +81,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--with-asan alias for --with-address-sanitier --with-asan alias for --with-address-sanitier
--enable-asan alias for --with-address-sanitier --enable-asan alias for --with-address-sanitier
--with-gcov build with gcov coverage enabled --with-gcov build with gcov coverage enabled
--with-actor-profiler enables the (experimental) actor_profiler API
Convenience options: Convenience options:
--dev-mode sets --build-type=debug, --no-examples, --dev-mode sets --build-type=debug, --no-examples,
...@@ -250,6 +251,9 @@ while [ $# -ne 0 ]; do ...@@ -250,6 +251,9 @@ while [ $# -ne 0 ]; do
--with-gcov) --with-gcov)
append_cache_entry CAF_ENABLE_GCOV BOOL yes append_cache_entry CAF_ENABLE_GCOV BOOL yes
;; ;;
--with-actor-profiler)
append_cache_entry CAF_ENABLE_ACTOR_PROFILER BOOL yes
;;
--no-memory-management) --no-memory-management)
append_cache_entry CAF_NO_MEM_MANAGEMENT BOOL yes append_cache_entry CAF_NO_MEM_MANAGEMENT BOOL yes
;; ;;
......
...@@ -7,6 +7,7 @@ file(GLOB_RECURSE LIBCAF_CORE_HDRS "caf/*.hpp") ...@@ -7,6 +7,7 @@ file(GLOB_RECURSE LIBCAF_CORE_HDRS "caf/*.hpp")
enum_to_string("caf/exit_reason.hpp" "exit_reason_strings.cpp") enum_to_string("caf/exit_reason.hpp" "exit_reason_strings.cpp")
enum_to_string("caf/intrusive/inbox_result.hpp" "inbox_result_strings.cpp") enum_to_string("caf/intrusive/inbox_result.hpp" "inbox_result_strings.cpp")
enum_to_string("caf/intrusive/task_result.hpp" "task_result_strings.cpp") enum_to_string("caf/intrusive/task_result.hpp" "task_result_strings.cpp")
enum_to_string("caf/invoke_message_result.hpp" "invoke_msg_result_strings.cpp")
enum_to_string("caf/message_priority.hpp" "message_priority_strings.cpp") enum_to_string("caf/message_priority.hpp" "message_priority_strings.cpp")
enum_to_string("caf/pec.hpp" "pec_strings.cpp") enum_to_string("caf/pec.hpp" "pec_strings.cpp")
enum_to_string("caf/sec.hpp" "sec_strings.cpp") enum_to_string("caf/sec.hpp" "sec_strings.cpp")
...@@ -16,6 +17,7 @@ enum_to_string("caf/stream_priority.hpp" "stream_priority_strings.cpp") ...@@ -16,6 +17,7 @@ enum_to_string("caf/stream_priority.hpp" "stream_priority_strings.cpp")
set(LIBCAF_CORE_SRCS set(LIBCAF_CORE_SRCS
"${CMAKE_CURRENT_BINARY_DIR}/exit_reason_strings.cpp" "${CMAKE_CURRENT_BINARY_DIR}/exit_reason_strings.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/inbox_result_strings.cpp" "${CMAKE_CURRENT_BINARY_DIR}/inbox_result_strings.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/invoke_msg_result_strings.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/message_priority_strings.cpp" "${CMAKE_CURRENT_BINARY_DIR}/message_priority_strings.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/pec_strings.cpp" "${CMAKE_CURRENT_BINARY_DIR}/pec_strings.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/sec_strings.cpp" "${CMAKE_CURRENT_BINARY_DIR}/sec_strings.cpp"
...@@ -33,6 +35,7 @@ set(LIBCAF_CORE_SRCS ...@@ -33,6 +35,7 @@ set(LIBCAF_CORE_SRCS
src/actor_control_block.cpp src/actor_control_block.cpp
src/actor_ostream.cpp src/actor_ostream.cpp
src/actor_pool.cpp src/actor_pool.cpp
src/actor_profiler.cpp
src/actor_proxy.cpp src/actor_proxy.cpp
src/actor_registry.cpp src/actor_registry.cpp
src/actor_system.cpp src/actor_system.cpp
......
...@@ -37,11 +37,13 @@ public: ...@@ -37,11 +37,13 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
explicit actor_config(execution_unit* ptr = nullptr); explicit actor_config(execution_unit* host = nullptr,
local_actor* parent = nullptr);
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
execution_unit* host; execution_unit* host;
local_actor* parent;
int flags; int flags;
input_range<const group>* groups; input_range<const group>* groups;
detail::unique_function<behavior(local_actor*)> init_fun; detail::unique_function<behavior(local_actor*)> init_fun;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 "caf/detail/build_config.hpp"
#include "caf/fwd.hpp"
namespace caf {
/// A profiler which provides a set of callbacks for several actor operations in
/// order to collect fine-grained profiling state about the system.
/// @experimental
class actor_profiler {
public:
virtual ~actor_profiler();
/// Called whenever the actor system spawns a new actor. The system calls this
/// member function after the constructor of `self` has completed but before
/// constructing the behavior.
/// @param self The new actor.
/// @param parent Points to the parent actor unless `self` is a top-level
/// actor (in this case, `parent` has the value `nullptr`).
/// @thread-safe
virtual void add_actor(const local_actor& self, const local_actor* parent)
= 0;
/// Called before the actor system calls the destructor for `self`.
/// @param ptr Points to an actor that is about to get destroyed.
/// @thread-safe
virtual void remove_actor(const local_actor& self) = 0;
/// Called whenever an actor is about to process an element from its mailbox.
/// @param self The current actor.
/// @param element The current element from the mailbox.
/// @thread-safe
virtual void before_processing(const local_actor& self,
const mailbox_element& element)
= 0;
/// Called after an actor processed an element from its mailbox.
/// @param self The current actor.
/// @param result Stores whether the actor consumed, skipped or dropped the
/// message.
/// @thread-safe
virtual void after_processing(const local_actor& self,
invoke_message_result result)
= 0;
};
#ifdef CAF_ENABLE_ACTOR_PROFILER
# define CAF_BEFORE_PROCESSING(self, msg) \
self->system().profiler_before_processing(*self, msg)
# define CAF_AFTER_PROCESSING(self, result) \
self->system().profiler_after_processing(*self, result)
#else
# define CAF_BEFORE_PROCESSING(self, msg) CAF_VOID_STMT
# define CAF_AFTER_PROCESSING(self, result) CAF_VOID_STMT
#endif
} // namespace caf
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "caf/actor_clock.hpp" #include "caf/actor_clock.hpp"
#include "caf/actor_config.hpp" #include "caf/actor_config.hpp"
#include "caf/actor_marker.hpp" #include "caf/actor_marker.hpp"
#include "caf/actor_profiler.hpp"
#include "caf/actor_registry.hpp" #include "caf/actor_registry.hpp"
#include "caf/composable_behavior_based_actor.hpp" #include "caf/composable_behavior_based_actor.hpp"
#include "caf/detail/init_fun_factory.hpp" #include "caf/detail/init_fun_factory.hpp"
...@@ -545,10 +546,35 @@ public: ...@@ -545,10 +546,35 @@ public:
auto res = make_actor<C>(next_actor_id(), node(), this, auto res = make_actor<C>(next_actor_id(), node(), this,
cfg, std::forward<Ts>(xs)...); cfg, std::forward<Ts>(xs)...);
auto ptr = static_cast<C*>(actor_cast<abstract_actor*>(res)); auto ptr = static_cast<C*>(actor_cast<abstract_actor*>(res));
#ifdef CAF_ENABLE_ACTOR_PROFILER
profiler_add_actor(*ptr, cfg.parent);
#endif
ptr->launch(cfg.host, has_lazy_init_flag(Os), has_hide_flag(Os)); ptr->launch(cfg.host, has_lazy_init_flag(Os), has_hide_flag(Os));
return res; return res;
} }
void profiler_add_actor(const local_actor& self, const local_actor* parent) {
if (profiler_)
profiler_->add_actor(self, parent);
}
void profiler_remove_actor(const local_actor& self) {
if (profiler_)
profiler_->remove_actor(self);
}
void profiler_before_processing(const local_actor& self,
const mailbox_element& element) {
if (profiler_)
profiler_->before_processing(self, element);
}
void profiler_after_processing(const local_actor& self,
invoke_message_result result) {
if (profiler_)
profiler_->after_processing(self, result);
}
/// @endcond /// @endcond
private: private:
...@@ -577,6 +603,9 @@ private: ...@@ -577,6 +603,9 @@ private:
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
/// Provides system-wide callbacks for several actor operations.
actor_profiler* profiler_;
/// Used to generate ascending actor IDs. /// Used to generate ascending actor IDs.
std::atomic<size_t> ids_; std::atomic<size_t> ids_;
...@@ -634,4 +663,3 @@ private: ...@@ -634,4 +663,3 @@ private:
}; };
} // namespace caf } // namespace caf
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <unordered_map> #include <unordered_map>
#include "caf/actor_factory.hpp" #include "caf/actor_factory.hpp"
#include "caf/actor_profiler.hpp"
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp" #include "caf/config_option_adder.hpp"
#include "caf/config_option_set.hpp" #include "caf/config_option_set.hpp"
...@@ -300,6 +301,11 @@ public: ...@@ -300,6 +301,11 @@ public:
thread_hooks thread_hooks_; thread_hooks thread_hooks_;
/// Provides system-wide callbacks for several actor operations.
/// @experimental
/// @note Has no effect unless building CAF with CAF_ENABLE_ACTOR_PROFILER.
actor_profiler* profiler = nullptr;
// -- run-time type information ---------------------------------------------- // -- run-time type information ----------------------------------------------
portable_name_map type_names_by_rtti; portable_name_map type_names_by_rtti;
......
...@@ -88,6 +88,7 @@ class actor_companion; ...@@ -88,6 +88,7 @@ class actor_companion;
class actor_config; class actor_config;
class actor_control_block; class actor_control_block;
class actor_pool; class actor_pool;
class actor_profiler;
class actor_proxy; class actor_proxy;
class actor_registry; class actor_registry;
class actor_system; class actor_system;
...@@ -177,6 +178,7 @@ enum class atom_value : uint64_t; ...@@ -177,6 +178,7 @@ enum class atom_value : uint64_t;
enum class byte : uint8_t; enum class byte : uint8_t;
enum class sec : uint8_t; enum class sec : uint8_t;
enum class stream_priority; enum class stream_priority;
enum class invoke_message_result;
// -- aliases ------------------------------------------------------------------ // -- aliases ------------------------------------------------------------------
......
...@@ -22,17 +22,21 @@ ...@@ -22,17 +22,21 @@
namespace caf { namespace caf {
enum invoke_message_result { /// Stores the result of a message invocation.
im_success, enum class invoke_message_result {
im_skipped, /// Indicates that the actor consumed the message.
im_dropped consumed,
/// Indicates that the actor left the message in the mailbox.
skipped,
/// Indicates that the actor discarded the message based on meta data. For
/// example, timeout messages for already received requests usually get
/// dropped without calling any user-defined code.
dropped,
}; };
inline std::string to_string(invoke_message_result x) { /// @relates invoke_message_result
return x == im_success ? "im_success" std::string to_string(invoke_message_result);
: (x == im_skipped ? "im_skipped" : "im_dropped" );
}
} // namespace caf } // namespace caf
...@@ -96,15 +96,17 @@ public: ...@@ -96,15 +96,17 @@ public:
template <class T, spawn_options Os = no_spawn_options, class... Ts> template <class T, spawn_options Os = no_spawn_options, class... Ts>
infer_handle_from_class_t<T> spawn(Ts&&... xs) { infer_handle_from_class_t<T> spawn(Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
return eval_opts(Os, system().spawn_class<T, make_unbound(Os)>( return eval_opts(Os,
cfg, std::forward<Ts>(xs)...)); system().spawn_class<T, make_unbound(Os)>(cfg,
std::forward<Ts>(
xs)...));
} }
template <class T, spawn_options Os = no_spawn_options> template <class T, spawn_options Os = no_spawn_options>
infer_handle_from_state_t<T> spawn() { infer_handle_from_state_t<T> spawn() {
using impl = composable_behavior_based_actor<T>; using impl = composable_behavior_based_actor<T>;
actor_config cfg{context()}; actor_config cfg{context(), this};
return eval_opts(Os, system().spawn_class<impl, make_unbound(Os)>(cfg)); return eval_opts(Os, system().spawn_class<impl, make_unbound(Os)>(cfg));
} }
...@@ -114,7 +116,7 @@ public: ...@@ -114,7 +116,7 @@ public:
static constexpr bool spawnable = detail::spawnable<F, impl, Ts...>(); static constexpr bool spawnable = detail::spawnable<F, impl, Ts...>();
static_assert(spawnable, static_assert(spawnable,
"cannot spawn function-based actor with given arguments"); "cannot spawn function-based actor with given arguments");
actor_config cfg{context()}; actor_config cfg{context(), this};
static constexpr spawn_options unbound = make_unbound(Os); static constexpr spawn_options unbound = make_unbound(Os);
detail::bool_token<spawnable> enabled; detail::bool_token<spawnable> enabled;
return eval_opts(Os, return eval_opts(Os,
...@@ -125,50 +127,65 @@ public: ...@@ -125,50 +127,65 @@ public:
template <class T, spawn_options Os = no_spawn_options, class Groups, template <class T, spawn_options Os = no_spawn_options, class Groups,
class... Ts> class... Ts>
actor spawn_in_groups(const Groups& gs, Ts&&... xs) { actor spawn_in_groups(const Groups& gs, Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
return eval_opts(Os, system().spawn_class_in_groups<T, make_unbound(Os)>( return eval_opts(Os, system()
cfg, gs.begin(), gs.end(), std::forward<Ts>(xs)...)); .spawn_class_in_groups<
T, make_unbound(Os)>(cfg, gs.begin(), gs.end(),
std::forward<Ts>(xs)...));
} }
template <class T, spawn_options Os = no_spawn_options, class... Ts> template <class T, spawn_options Os = no_spawn_options, class... Ts>
actor spawn_in_groups(std::initializer_list<group> gs, Ts&&... xs) { actor spawn_in_groups(std::initializer_list<group> gs, Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
return eval_opts(Os, system().spawn_class_in_groups<T, make_unbound(Os)>( return eval_opts(Os, system()
cfg, gs.begin(), gs.end(), std::forward<Ts>(xs)...)); .spawn_class_in_groups<
T, make_unbound(Os)>(cfg, gs.begin(), gs.end(),
std::forward<Ts>(xs)...));
} }
template <class T, spawn_options Os = no_spawn_options, class... Ts> template <class T, spawn_options Os = no_spawn_options, class... Ts>
actor spawn_in_group(const group& grp, Ts&&... xs) { actor spawn_in_group(const group& grp, Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
auto first = &grp; auto first = &grp;
return eval_opts(Os, system().spawn_class_in_groups<T, make_unbound(Os)>( return eval_opts(Os, system()
cfg, first, first + 1, std::forward<Ts>(xs)...)); .spawn_class_in_groups<
T, make_unbound(Os)>(cfg, first, first + 1,
std::forward<Ts>(xs)...));
} }
template <spawn_options Os = no_spawn_options, class Groups, class F, template <spawn_options Os = no_spawn_options, class Groups, class F,
class... Ts> class... Ts>
actor spawn_in_groups(const Groups& gs, F fun, Ts&&... xs) { actor spawn_in_groups(const Groups& gs, F fun, Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
return eval_opts( return eval_opts(Os,
Os, system().spawn_fun_in_groups<make_unbound(Os)>( system()
cfg, gs.begin(), gs.end(), fun, std::forward<Ts>(xs)...)); .spawn_fun_in_groups<make_unbound(Os)>(cfg, gs.begin(),
gs.end(), fun,
std::forward<Ts>(
xs)...));
} }
template <spawn_options Os = no_spawn_options, class F, class... Ts> template <spawn_options Os = no_spawn_options, class F, class... Ts>
actor spawn_in_groups(std::initializer_list<group> gs, F fun, Ts&&... xs) { actor spawn_in_groups(std::initializer_list<group> gs, F fun, Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
return eval_opts( return eval_opts(Os,
Os, system().spawn_fun_in_groups<make_unbound(Os)>( system()
cfg, gs.begin(), gs.end(), fun, std::forward<Ts>(xs)...)); .spawn_fun_in_groups<make_unbound(Os)>(cfg, gs.begin(),
gs.end(), fun,
std::forward<Ts>(
xs)...));
} }
template <spawn_options Os = no_spawn_options, class F, class... Ts> template <spawn_options Os = no_spawn_options, class F, class... Ts>
actor spawn_in_group(const group& grp, F fun, Ts&&... xs) { actor spawn_in_group(const group& grp, F fun, Ts&&... xs) {
actor_config cfg{context()}; actor_config cfg{context(), this};
auto first = &grp; auto first = &grp;
return eval_opts(Os, return eval_opts(Os,
system().spawn_fun_in_groups<make_unbound(Os)>( system()
cfg, first, first + 1, fun, std::forward<Ts>(xs)...)); .spawn_fun_in_groups<make_unbound(Os)>(cfg, first,
first + 1, fun,
std::forward<Ts>(
xs)...));
} }
// -- sending asynchronous messages ------------------------------------------ // -- sending asynchronous messages ------------------------------------------
...@@ -333,10 +350,8 @@ public: ...@@ -333,10 +350,8 @@ public:
/// Return type is deduced from arguments. /// Return type is deduced from arguments.
/// Return value is implicitly convertible to untyped response promise. /// Return value is implicitly convertible to untyped response promise.
template <class... Ts, template <class... Ts,
class R = class R = typename detail::make_response_promise_helper<
typename detail::make_response_promise_helper< typename std::decay<Ts>::type...>::type>
typename std::decay<Ts>::type...
>::type>
R response(Ts&&... xs) { R response(Ts&&... xs) {
auto promise = make_response_promise<R>(); auto promise = make_response_promise<R>();
promise.deliver(std::forward<Ts>(xs)...); promise.deliver(std::forward<Ts>(xs)...);
...@@ -382,27 +397,23 @@ public: ...@@ -382,27 +397,23 @@ public:
return (mid.is_request()) ? mid.response_id() : message_id(); return (mid.is_request()) ? mid.response_id() : message_id();
} }
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");
auto mid = current_element_->mid; auto mid = current_element_->mid;
current_element_->mid = P == message_priority::high current_element_->mid = P == message_priority::high
? mid.with_high_priority() ? mid.with_high_priority()
: mid.with_normal_priority(); : mid.with_normal_priority();
dest->enqueue(make_mailbox_element(std::move(current_element_->sender), dest->enqueue(make_mailbox_element(std::move(current_element_->sender), mid,
mid, std::move(current_element_->stages), std::move(current_element_->stages),
std::forward<Ts>(xs)...), std::forward<Ts>(xs)...),
context()); context());
return {}; return {};
......
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
#pragma once #pragma once
#include <thread>
#include <fstream>
#include <cstring> #include <cstring>
#include <sstream> #include <fstream>
#include <iostream> #include <iostream>
#include <typeinfo> #include <sstream>
#include <thread>
#include <type_traits> #include <type_traits>
#include <typeinfo>
#include <unordered_map> #include <unordered_map>
#include "caf/abstract_actor.hpp" #include "caf/abstract_actor.hpp"
...@@ -282,10 +282,9 @@ public: ...@@ -282,10 +282,9 @@ public:
/// Returns a string representation of the joined groups of `x` if `x` is an /// Returns a string representation of the joined groups of `x` if `x` is an
/// actor with the `subscriber` mixin. /// actor with the `subscriber` mixin.
template <class T> template <class T>
static typename std::enable_if< static
std::is_base_of<mixin::subscriber_base, T>::value, typename std::enable_if<std::is_base_of<mixin::subscriber_base, T>::value,
std::string std::string>::type
>::type
joined_groups_of(const T& x) { joined_groups_of(const T& x) {
return deep_to_string(x.joined_groups()); return deep_to_string(x.joined_groups());
} }
...@@ -293,10 +292,9 @@ public: ...@@ -293,10 +292,9 @@ public:
/// Returns a string representation of an empty list if `x` is not an actor /// Returns a string representation of an empty list if `x` is not an actor
/// with the `subscriber` mixin. /// with the `subscriber` mixin.
template <class T> template <class T>
static typename std::enable_if< static
!std::is_base_of<mixin::subscriber_base, T>::value, typename std::enable_if<!std::is_base_of<mixin::subscriber_base, T>::value,
const char* const char*>::type
>::type
joined_groups_of(const T& x) { joined_groups_of(const T& x) {
CAF_IGNORE_UNUSED(x); CAF_IGNORE_UNUSED(x);
return "[]"; return "[]";
...@@ -402,7 +400,7 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -402,7 +400,7 @@ bool operator==(const logger::field& x, const logger::field& y);
#ifndef CAF_LOG_COMPONENT #ifndef CAF_LOG_COMPONENT
/// Name of the current component when logging. /// Name of the current component when logging.
#define CAF_LOG_COMPONENT "caf" # define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT #endif // CAF_LOG_COMPONENT
// -- utility macros ----------------------------------------------------------- // -- utility macros -----------------------------------------------------------
...@@ -410,23 +408,22 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -410,23 +408,22 @@ bool operator==(const logger::field& x, const logger::field& y);
#ifdef CAF_MSVC #ifdef CAF_MSVC
/// Expands to a string representation of the current funciton name that /// Expands to a string representation of the current funciton name that
/// includes the full function name and its signature. /// includes the full function name and its signature.
#define CAF_PRETTY_FUN __FUNCSIG__ # define CAF_PRETTY_FUN __FUNCSIG__
#else // CAF_MSVC #else // CAF_MSVC
/// Expands to a string representation of the current funciton name that /// Expands to a string representation of the current funciton name that
/// includes the full function name and its signature. /// includes the full function name and its signature.
#define CAF_PRETTY_FUN __PRETTY_FUNCTION__ # define CAF_PRETTY_FUN __PRETTY_FUNCTION__
#endif // CAF_MSVC #endif // CAF_MSVC
/// Concatenates `a` and `b` to a single preprocessor token. /// Concatenates `a` and `b` to a single preprocessor token.
#define CAF_CAT(a, b) a##b #define CAF_CAT(a, b) a##b
#define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message) \ #define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message) \
::caf::logger::event ( \ ::caf::logger::event(loglvl, __LINE__, caf::atom(component), CAF_PRETTY_FUN, \
loglvl, __LINE__, caf::atom(component), CAF_PRETTY_FUN, __func__, \ __func__, caf::logger::skip_path(__FILE__), \
caf::logger::skip_path(__FILE__), \
(::caf::logger::line_builder{} << message).get(), \ (::caf::logger::line_builder{} << message).get(), \
::std::this_thread::get_id(), aid, ::caf::make_timestamp() \ ::std::this_thread::get_id(), aid, \
) ::caf::make_timestamp())
/// Expands to `argument = <argument>` in log output. /// Expands to `argument = <argument>` in log output.
#define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument) #define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument)
...@@ -474,11 +471,11 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -474,11 +471,11 @@ bool operator==(const logger::field& x, const logger::field& y);
#if CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE #if CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE
#define CAF_LOG_TRACE(unused) CAF_VOID_STMT # define CAF_LOG_TRACE(unused) CAF_VOID_STMT
#else // CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE #else // CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE
#define CAF_LOG_TRACE(entry_message) \ # define CAF_LOG_TRACE(entry_message) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_TRACE, \ CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_TRACE, \
"ENTRY" << entry_message); \ "ENTRY" << entry_message); \
auto CAF_UNIFYN(caf_log_trace_guard_) = ::caf::detail::make_scope_guard( \ auto CAF_UNIFYN(caf_log_trace_guard_) = ::caf::detail::make_scope_guard( \
...@@ -487,17 +484,17 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -487,17 +484,17 @@ bool operator==(const logger::field& x, const logger::field& y);
#endif // CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE #endif // CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE
#if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
#define CAF_LOG_DEBUG(output) \ # define CAF_LOG_DEBUG(output) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output) CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output)
#endif #endif
#if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_INFO #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_INFO
#define CAF_LOG_INFO(output) \ # define CAF_LOG_INFO(output) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output) CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output)
#endif #endif
#if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_WARNING #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_WARNING
#define CAF_LOG_WARNING(output) \ # define CAF_LOG_WARNING(output) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output) CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output)
#endif #endif
...@@ -505,40 +502,40 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -505,40 +502,40 @@ bool operator==(const logger::field& x, const logger::field& y);
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output) CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output)
#ifndef CAF_LOG_INFO #ifndef CAF_LOG_INFO
#define CAF_LOG_INFO(output) CAF_VOID_STMT # define CAF_LOG_INFO(output) CAF_VOID_STMT
#define CAF_LOG_INFO_IF(cond, output) CAF_VOID_STMT # define CAF_LOG_INFO_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_INFO #else // CAF_LOG_INFO
#define CAF_LOG_INFO_IF(cond, output) \ # define CAF_LOG_INFO_IF(cond, output) \
if (cond) \ if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output); \ CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output); \
CAF_VOID_STMT CAF_VOID_STMT
#endif // CAF_LOG_INFO #endif // CAF_LOG_INFO
#ifndef CAF_LOG_DEBUG #ifndef CAF_LOG_DEBUG
#define CAF_LOG_DEBUG(output) CAF_VOID_STMT # define CAF_LOG_DEBUG(output) CAF_VOID_STMT
#define CAF_LOG_DEBUG_IF(cond, output) CAF_VOID_STMT # define CAF_LOG_DEBUG_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_DEBUG #else // CAF_LOG_DEBUG
#define CAF_LOG_DEBUG_IF(cond, output) \ # define CAF_LOG_DEBUG_IF(cond, output) \
if (cond) \ if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output); \ CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output); \
CAF_VOID_STMT CAF_VOID_STMT
#endif // CAF_LOG_DEBUG #endif // CAF_LOG_DEBUG
#ifndef CAF_LOG_WARNING #ifndef CAF_LOG_WARNING
#define CAF_LOG_WARNING(output) CAF_VOID_STMT # define CAF_LOG_WARNING(output) CAF_VOID_STMT
#define CAF_LOG_WARNING_IF(cond, output) CAF_VOID_STMT # define CAF_LOG_WARNING_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_WARNING #else // CAF_LOG_WARNING
#define CAF_LOG_WARNING_IF(cond, output) \ # define CAF_LOG_WARNING_IF(cond, output) \
if (cond) \ if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output); \ CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output); \
CAF_VOID_STMT CAF_VOID_STMT
#endif // CAF_LOG_WARNING #endif // CAF_LOG_WARNING
#ifndef CAF_LOG_ERROR #ifndef CAF_LOG_ERROR
#define CAF_LOG_ERROR(output) CAF_VOID_STMT # define CAF_LOG_ERROR(output) CAF_VOID_STMT
#define CAF_LOG_ERROR_IF(cond, output) CAF_VOID_STMT # define CAF_LOG_ERROR_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_ERROR #else // CAF_LOG_ERROR
#define CAF_LOG_ERROR_IF(cond, output) \ # define CAF_LOG_ERROR_IF(cond, output) \
if (cond) \ if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output); \ CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output); \
CAF_VOID_STMT CAF_VOID_STMT
...@@ -552,73 +549,87 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -552,73 +549,87 @@ bool operator==(const logger::field& x, const logger::field& y);
#if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
#define CAF_LOG_SPAWN_EVENT(ref, ctor_data) \ # define CAF_LOG_SPAWN_EVENT(ref, ctor_data) \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \ CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \
"SPAWN ; ID =" \ "SPAWN ; ID =" \
<< ref.id() << "; NAME =" << ref.name() \ << ref.id() << "; NAME =" << ref.name() << "; TYPE =" \
<< "; TYPE =" << ::caf::detail::pretty_type_name(typeid(ref)) \ << ::caf::detail::pretty_type_name(typeid(ref)) \
<< "; ARGS =" << ctor_data.c_str() \ << "; ARGS =" << ctor_data.c_str() \
<< "; NODE =" << ref.node() \ << "; NODE =" << ref.node() \
<< "; GROUPS =" << ::caf::logger::joined_groups_of(ref)) << "; GROUPS =" << ::caf::logger::joined_groups_of(ref))
#define CAF_LOG_SEND_EVENT(ptr) \ # define CAF_LOG_SEND_EVENT(ptr) \
CAF_LOG_IMPL( \ CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \
CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \
"SEND ; TO =" \ "SEND ; TO =" \
<< ::caf::deep_to_string(::caf::strong_actor_ptr{this->ctrl()}).c_str() \ << ::caf::deep_to_string( \
::caf::strong_actor_ptr{this->ctrl()}) \
.c_str() \
<< "; FROM =" << ::caf::deep_to_string(ptr->sender).c_str() \ << "; FROM =" << ::caf::deep_to_string(ptr->sender).c_str() \
<< "; STAGES =" << ::caf::deep_to_string(ptr->stages).c_str() \ << "; STAGES =" \
<< "; CONTENT =" << ::caf::deep_to_string(ptr->content()).c_str()) << ::caf::deep_to_string(ptr->stages).c_str() \
<< "; CONTENT =" \
<< ::caf::deep_to_string(ptr->content()).c_str())
#define CAF_LOG_RECEIVE_EVENT(ptr) \ # define CAF_LOG_RECEIVE_EVENT(ptr) \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \ CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \
"RECEIVE ; FROM =" \ "RECEIVE ; FROM =" \
<< ::caf::deep_to_string(ptr->sender).c_str() \ << ::caf::deep_to_string(ptr->sender).c_str() \
<< "; STAGES =" << ::caf::deep_to_string(ptr->stages).c_str() \ << "; STAGES =" \
<< ::caf::deep_to_string(ptr->stages).c_str() \
<< "; CONTENT =" \ << "; CONTENT =" \
<< ::caf::deep_to_string(ptr->content()).c_str()) << ::caf::deep_to_string(ptr->content()).c_str())
#define CAF_LOG_REJECT_EVENT() \ # define CAF_LOG_REJECT_EVENT() \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "REJECT") CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "REJECT")
#define CAF_LOG_ACCEPT_EVENT(unblocked) \ # define CAF_LOG_ACCEPT_EVENT(unblocked) \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \ CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \
"ACCEPT ; UNBLOCKED =" << unblocked) "ACCEPT ; UNBLOCKED =" << unblocked)
#define CAF_LOG_DROP_EVENT() \ # define CAF_LOG_DROP_EVENT() \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "DROP") CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "DROP")
#define CAF_LOG_SKIP_EVENT() \ # define CAF_LOG_SKIP_EVENT() \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "SKIP") CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "SKIP")
#define CAF_LOG_FINALIZE_EVENT() \ # define CAF_LOG_FINALIZE_EVENT() \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "FINALIZE") CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "FINALIZE")
#define CAF_LOG_TERMINATE_EVENT(thisptr, rsn) \ # define CAF_LOG_SKIP_OR_FINALIZE_EVENT(invoke_result) \
do { \
if (invoke_result == caf::invoke_message_result::skipped) \
CAF_LOG_SKIP_EVENT(); \
else \
CAF_LOG_FINALIZE_EVENT(); \
} while (false)
# define CAF_LOG_TERMINATE_EVENT(thisptr, rsn) \
CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \ CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, \
"TERMINATE ; ID =" << thisptr->id() \ "TERMINATE ; ID =" << thisptr->id() << "; REASON =" \
<< "; REASON =" << deep_to_string(rsn).c_str() \ << deep_to_string(rsn).c_str() \
<< "; NODE =" << thisptr->node()) << "; NODE =" << thisptr->node())
#else // CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG #else // CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
#define CAF_LOG_SPAWN_EVENT(ref, ctor_data) CAF_VOID_STMT # define CAF_LOG_SPAWN_EVENT(ref, ctor_data) CAF_VOID_STMT
# define CAF_LOG_SEND_EVENT(ptr) CAF_VOID_STMT
#define CAF_LOG_SEND_EVENT(ptr) CAF_VOID_STMT # define CAF_LOG_RECEIVE_EVENT(ptr) CAF_VOID_STMT
#define CAF_LOG_RECEIVE_EVENT(ptr) CAF_VOID_STMT # define CAF_LOG_REJECT_EVENT() CAF_VOID_STMT
#define CAF_LOG_REJECT_EVENT() CAF_VOID_STMT # define CAF_LOG_ACCEPT_EVENT(unblocked) CAF_VOID_STMT
#define CAF_LOG_ACCEPT_EVENT(unblocked) CAF_VOID_STMT # define CAF_LOG_DROP_EVENT() CAF_VOID_STMT
#define CAF_LOG_DROP_EVENT() CAF_VOID_STMT # define CAF_LOG_SKIP_EVENT() CAF_VOID_STMT
#define CAF_LOG_SKIP_EVENT() CAF_VOID_STMT # define CAF_LOG_SKIP_OR_FINALIZE_EVENT(unused) CAF_VOID_STMT
#define CAF_LOG_FINALIZE_EVENT() CAF_VOID_STMT # define CAF_LOG_FINALIZE_EVENT() CAF_VOID_STMT
#define CAF_LOG_TERMINATE_EVENT(thisptr, rsn) CAF_VOID_STMT # define CAF_LOG_TERMINATE_EVENT(thisptr, rsn) CAF_VOID_STMT
#endif // CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG #endif // CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
...@@ -629,12 +640,12 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -629,12 +640,12 @@ bool operator==(const logger::field& x, const logger::field& y);
#define CAF_LOG_STREAM_COMPONENT "caf_stream" #define CAF_LOG_STREAM_COMPONENT "caf_stream"
#if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
#define CAF_STREAM_LOG_DEBUG(output) \ # define CAF_STREAM_LOG_DEBUG(output) \
CAF_LOG_IMPL(CAF_LOG_STREAM_COMPONENT, CAF_LOG_LEVEL_DEBUG, output) CAF_LOG_IMPL(CAF_LOG_STREAM_COMPONENT, CAF_LOG_LEVEL_DEBUG, output)
#define CAF_STREAM_LOG_DEBUG_IF(condition, output) \ # define CAF_STREAM_LOG_DEBUG_IF(condition, output) \
if (condition) \ if (condition) \
CAF_LOG_IMPL(CAF_LOG_STREAM_COMPONENT, CAF_LOG_LEVEL_DEBUG, output) CAF_LOG_IMPL(CAF_LOG_STREAM_COMPONENT, CAF_LOG_LEVEL_DEBUG, output)
#else #else
#define CAF_STREAM_LOG_DEBUG(unused) CAF_VOID_STMT # define CAF_STREAM_LOG_DEBUG(unused) CAF_VOID_STMT
#define CAF_STREAM_LOG_DEBUG_IF(unused1, unused2) CAF_VOID_STMT # define CAF_STREAM_LOG_DEBUG_IF(unused1, unused2) CAF_VOID_STMT
#endif #endif
...@@ -22,8 +22,9 @@ ...@@ -22,8 +22,9 @@
namespace caf { namespace caf {
actor_config::actor_config(execution_unit* ptr) actor_config::actor_config(execution_unit* host, local_actor* parent)
: host(ptr), : host(host),
parent(parent),
flags(abstract_channel::is_abstract_actor_flag), flags(abstract_channel::is_abstract_actor_flag),
groups(nullptr) { groups(nullptr) {
// nop // nop
...@@ -33,12 +34,9 @@ std::string to_string(const actor_config& x) { ...@@ -33,12 +34,9 @@ std::string to_string(const actor_config& x) {
// Note: x.groups is an input range. Traversing it is emptying it, hence we // Note: x.groups is an input range. Traversing it is emptying it, hence we
// cannot look inside the range here. // cannot look inside the range here.
std::string result = "actor_config("; std::string result = "actor_config(";
bool first = false;
auto add = [&](int flag, const char* name) { auto add = [&](int flag, const char* name) {
if ((x.flags & flag) != 0) { if ((x.flags & flag) != 0) {
if (first) if (result.back() != '(')
first = false;
else
result += ", "; result += ", ";
result += name; result += name;
} }
...@@ -48,7 +46,7 @@ std::string to_string(const actor_config& x) { ...@@ -48,7 +46,7 @@ std::string to_string(const actor_config& x) {
add(abstract_actor::is_detached_flag, "detached_flag"); add(abstract_actor::is_detached_flag, "detached_flag");
add(abstract_actor::is_blocking_flag, "blocking_flag"); add(abstract_actor::is_blocking_flag, "blocking_flag");
add(abstract_actor::is_hidden_flag, "hidden_flag"); add(abstract_actor::is_hidden_flag, "hidden_flag");
result += ")"; result += ')';
return result; return result;
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#include "caf/actor_profiler.hpp"
namespace caf {
actor_profiler::~actor_profiler() {
// nop
}
} // namespace caf
...@@ -216,7 +216,8 @@ const char* actor_system::module::name() const noexcept { ...@@ -216,7 +216,8 @@ const char* actor_system::module::name() const noexcept {
} }
actor_system::actor_system(actor_system_config& cfg) actor_system::actor_system(actor_system_config& cfg)
: ids_(0), : profiler_(cfg.profiler),
ids_(0),
types_(*this), types_(*this),
logger_(new caf::logger(*this), false), logger_(new caf::logger(*this), false),
registry_(*this), registry_(*this),
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "caf/detail/invoke_result_visitor.hpp" #include "caf/detail/invoke_result_visitor.hpp"
#include "caf/detail/set_thread_name.hpp" #include "caf/detail/set_thread_name.hpp"
#include "caf/detail/sync_request_bouncer.hpp" #include "caf/detail/sync_request_bouncer.hpp"
#include "caf/invoke_message_result.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
namespace caf { namespace caf {
...@@ -156,9 +157,12 @@ void blocking_actor::fail_state(error err) { ...@@ -156,9 +157,12 @@ void blocking_actor::fail_state(error err) {
} }
intrusive::task_result intrusive::task_result
blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) { blocking_actor::mailbox_visitor::operator()(mailbox_element& x) {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
CAF_LOG_RECEIVE_EVENT((&x)); CAF_LOG_RECEIVE_EVENT((&x));
CAF_BEFORE_PROCESSING(self, x);
// Wrap the actual body for the function.
auto body = [this, &x] {
auto check_if_done = [&]() -> intrusive::task_result { auto check_if_done = [&]() -> intrusive::task_result {
// Stop consuming items when reaching the end of the user-defined receive // Stop consuming items when reaching the end of the user-defined receive
// loop either via post or pre condition. // loop either via post or pre condition.
...@@ -170,11 +174,9 @@ blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) { ...@@ -170,11 +174,9 @@ blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) {
// Skip messages that don't match our message ID. // Skip messages that don't match our message ID.
if (mid.is_response()) { if (mid.is_response()) {
if (mid != x.mid) { if (mid != x.mid) {
CAF_LOG_SKIP_EVENT();
return intrusive::task_result::skip; return intrusive::task_result::skip;
} }
} else if (x.mid.is_response()) { } else if (x.mid.is_response()) {
CAF_LOG_SKIP_EVENT();
return intrusive::task_result::skip; return intrusive::task_result::skip;
} }
// Automatically unlink from actors after receiving an exit. // Automatically unlink from actors after receiving an exit.
...@@ -183,20 +185,18 @@ blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) { ...@@ -183,20 +185,18 @@ blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) {
// Blocking actors can nest receives => push/pop `current_element_` // Blocking actors can nest receives => push/pop `current_element_`
auto prev_element = self->current_element_; auto prev_element = self->current_element_;
self->current_element_ = &x; self->current_element_ = &x;
auto g = detail::make_scope_guard([&] { auto g = detail::make_scope_guard(
self->current_element_ = prev_element; [&] { self->current_element_ = prev_element; });
});
// Dispatch on x. // Dispatch on x.
detail::default_invoke_result_visitor<blocking_actor> visitor{self}; detail::default_invoke_result_visitor<blocking_actor> visitor{self};
switch (bhvr.nested(visitor, x.content())) { switch (bhvr.nested(visitor, x.content())) {
default: default:
return check_if_done(); return check_if_done();
case match_case::no_match: case match_case::no_match: { // Blocking actors can have fallback handlers
{ // Blocking actors can have fallback handlers for catch-all rules. // for catch-all rules.
auto sres = bhvr.fallback(*self->current_element_); auto sres = bhvr.fallback(*self->current_element_);
if (sres.flag != rt_skip) { if (sres.flag != rt_skip) {
visitor.visit(sres); visitor.visit(sres);
CAF_LOG_FINALIZE_EVENT();
return check_if_done(); return check_if_done();
} }
} }
...@@ -209,14 +209,23 @@ blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) { ...@@ -209,14 +209,23 @@ blocking_actor::mailbox_visitor:: operator()(mailbox_element& x) {
std::move(x.stages), err}; std::move(x.stages), err};
self->current_element_ = &tmp; self->current_element_ = &tmp;
bhvr.nested(tmp.content()); bhvr.nested(tmp.content());
CAF_LOG_FINALIZE_EVENT();
return check_if_done(); return check_if_done();
} }
CAF_ANNOTATE_FALLTHROUGH; CAF_ANNOTATE_FALLTHROUGH;
case match_case::skip: case match_case::skip:
CAF_LOG_SKIP_EVENT();
return intrusive::task_result::skip; return intrusive::task_result::skip;
} }
};
// Post-process the returned value from the function body.
auto result = body();
if (result == intrusive::task_result::skip) {
CAF_AFTER_PROCESSING(self, invoke_message_result::skipped);
CAF_LOG_SKIP_EVENT();
} else {
CAF_AFTER_PROCESSING(self, invoke_message_result::consumed);
CAF_LOG_FINALIZE_EVENT();
}
return result;
} }
void blocking_actor::receive_impl(receive_cond& rcc, void blocking_actor::receive_impl(receive_cond& rcc,
......
...@@ -18,21 +18,21 @@ ...@@ -18,21 +18,21 @@
#include "caf/local_actor.hpp" #include "caf/local_actor.hpp"
#include <string>
#include <condition_variable> #include <condition_variable>
#include <string>
#include "caf/sec.hpp"
#include "caf/atom.hpp"
#include "caf/logger.hpp"
#include "caf/scheduler.hpp"
#include "caf/resumable.hpp"
#include "caf/actor_cast.hpp" #include "caf/actor_cast.hpp"
#include "caf/exit_reason.hpp"
#include "caf/actor_system.hpp"
#include "caf/actor_ostream.hpp" #include "caf/actor_ostream.hpp"
#include "caf/actor_system.hpp"
#include "caf/atom.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp" #include "caf/binary_serializer.hpp"
#include "caf/default_attachable.hpp" #include "caf/default_attachable.hpp"
#include "caf/binary_deserializer.hpp" #include "caf/exit_reason.hpp"
#include "caf/logger.hpp"
#include "caf/resumable.hpp"
#include "caf/scheduler.hpp"
#include "caf/sec.hpp"
namespace caf { namespace caf {
...@@ -50,6 +50,9 @@ local_actor::~local_actor() { ...@@ -50,6 +50,9 @@ local_actor::~local_actor() {
void local_actor::on_destroy() { void local_actor::on_destroy() {
CAF_PUSH_AID_FROM_PTR(this); CAF_PUSH_AID_FROM_PTR(this);
#ifdef CAF_ENABLE_ACTOR_PROFILER
system().profiler_remove_actor(*this);
#endif
if (!getf(is_cleaned_up_flag)) { if (!getf(is_cleaned_up_flag)) {
on_exit(); on_exit();
cleanup(exit_reason::unreachable, nullptr); cleanup(exit_reason::unreachable, nullptr);
...@@ -68,15 +71,16 @@ void local_actor::request_response_timeout(const duration& d, message_id mid) { ...@@ -68,15 +71,16 @@ void local_actor::request_response_timeout(const duration& d, message_id mid) {
void local_actor::monitor(abstract_actor* ptr, message_priority priority) { void local_actor::monitor(abstract_actor* ptr, message_priority priority) {
if (ptr != nullptr) if (ptr != nullptr)
ptr->attach(default_attachable::make_monitor(ptr->address(), address(), ptr->attach(
priority)); default_attachable::make_monitor(ptr->address(), address(), priority));
} }
void local_actor::demonitor(const actor_addr& whom) { void local_actor::demonitor(const actor_addr& whom) {
CAF_LOG_TRACE(CAF_ARG(whom)); CAF_LOG_TRACE(CAF_ARG(whom));
auto ptr = actor_cast<strong_actor_ptr>(whom); auto ptr = actor_cast<strong_actor_ptr>(whom);
if (ptr) { if (ptr) {
default_attachable::observe_token tk{address(), default_attachable::monitor}; default_attachable::observe_token tk{address(),
default_attachable::monitor};
ptr->get()->detach(tk); ptr->get()->detach(tk);
} }
} }
......
...@@ -31,35 +31,38 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -31,35 +31,38 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
current_element_ = &x; current_element_ = &x;
CAF_LOG_RECEIVE_EVENT(current_element_); CAF_LOG_RECEIVE_EVENT(current_element_);
CAF_BEFORE_PROCESSING(this, x);
// Wrap the actual body for the function.
auto body = [this, &x] {
// short-circuit awaited responses // short-circuit awaited responses
if (!awaited_responses_.empty()) { if (!awaited_responses_.empty()) {
auto& pr = awaited_responses_.front(); auto& pr = awaited_responses_.front();
// skip all messages until we receive the currently awaited response // skip all messages until we receive the currently awaited response
if (x.mid != pr.first) if (x.mid != pr.first)
return im_skipped; return invoke_message_result::skipped;
if (!pr.second(x.content())) { if (!pr.second(x.content())) {
// try again with error if first attempt failed // try again with error if first attempt failed
auto msg = make_message(make_error(sec::unexpected_response, auto msg = make_message(
x.move_content_to_message())); make_error(sec::unexpected_response, x.move_content_to_message()));
pr.second(msg); pr.second(msg);
} }
awaited_responses_.pop_front(); awaited_responses_.pop_front();
return im_success; return invoke_message_result::consumed;
} }
// handle multiplexed responses // handle multiplexed responses
if (x.mid.is_response()) { if (x.mid.is_response()) {
auto mrh = multiplexed_responses_.find(x.mid); auto mrh = multiplexed_responses_.find(x.mid);
// neither awaited nor multiplexed, probably an expired timeout // neither awaited nor multiplexed, probably an expired timeout
if (mrh == multiplexed_responses_.end()) if (mrh == multiplexed_responses_.end())
return im_dropped; return invoke_message_result::dropped;
if (!mrh->second(x.content())) { if (!mrh->second(x.content())) {
// try again with error if first attempt failed // try again with error if first attempt failed
auto msg = make_message(make_error(sec::unexpected_response, auto msg = make_message(
x.move_content_to_message())); make_error(sec::unexpected_response, x.move_content_to_message()));
mrh->second(msg); mrh->second(msg);
} }
multiplexed_responses_.erase(mrh); multiplexed_responses_.erase(mrh);
return im_success; return invoke_message_result::consumed;
} }
auto& content = x.content(); auto& content = x.content();
// handle timeout messages // handle timeout messages
...@@ -70,12 +73,12 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -70,12 +73,12 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
if (is_active_receive_timeout(tid)) { if (is_active_receive_timeout(tid)) {
CAF_LOG_DEBUG("handle timeout message"); CAF_LOG_DEBUG("handle timeout message");
if (bhvr_stack_.empty()) if (bhvr_stack_.empty())
return im_dropped; return invoke_message_result::dropped;
bhvr_stack_.back().handle_timeout(); bhvr_stack_.back().handle_timeout();
return im_success; return invoke_message_result::consumed;
} }
CAF_LOG_DEBUG("dropped expired timeout message"); CAF_LOG_DEBUG("dropped expired timeout message");
return im_dropped; return invoke_message_result::dropped;
} }
// handle everything else as ordinary message // handle everything else as ordinary message
detail::default_invoke_result_visitor<event_based_actor> visitor{this}; detail::default_invoke_result_visitor<event_based_actor> visitor{this};
...@@ -103,7 +106,8 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -103,7 +106,8 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
}; };
if (bhvr_stack_.empty()) { if (bhvr_stack_.empty()) {
call_default_handler(); call_default_handler();
return !skipped ? im_success : im_skipped; return !skipped ? invoke_message_result::consumed
: invoke_message_result::skipped;
} }
auto& bhvr = bhvr_stack_.back(); auto& bhvr = bhvr_stack_.back();
switch (bhvr(visitor, x.content())) { switch (bhvr(visitor, x.content())) {
...@@ -115,9 +119,14 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -115,9 +119,14 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
case match_case::no_match: case match_case::no_match:
call_default_handler(); call_default_handler();
} }
return !skipped ? im_success : im_skipped; return !skipped ? invoke_message_result::consumed
// should be unreachable : invoke_message_result::skipped;
CAF_CRITICAL("invalid message type"); };
// Post-process the returned value from the function body.
auto result = body();
CAF_AFTER_PROCESSING(this, result);
CAF_LOG_SKIP_OR_FINALIZE_EVENT(result);
return result;
} }
} // namespace caf } // namespace caf
...@@ -47,8 +47,7 @@ result<message> reflect_and_quit(scheduled_actor* ptr, message_view& x) { ...@@ -47,8 +47,7 @@ result<message> reflect_and_quit(scheduled_actor* ptr, message_view& x) {
result<message> print_and_drop(scheduled_actor* ptr, message_view& x) { result<message> print_and_drop(scheduled_actor* ptr, message_view& x) {
CAF_LOG_WARNING("unexpected message" << CAF_ARG(x.content())); CAF_LOG_WARNING("unexpected message" << CAF_ARG(x.content()));
aout(ptr) << "*** unexpected message [id: " << ptr->id() aout(ptr) << "*** unexpected message [id: " << ptr->id()
<< ", name: " << ptr->name() << "]: " << ", name: " << ptr->name() << "]: " << x.content().stringify()
<< x.content().stringify()
<< std::endl; << std::endl;
return sec::unexpected_message; return sec::unexpected_message;
} }
...@@ -90,7 +89,7 @@ void scheduled_actor::default_exit_handler(scheduled_actor* ptr, exit_msg& x) { ...@@ -90,7 +89,7 @@ void scheduled_actor::default_exit_handler(scheduled_actor* ptr, exit_msg& x) {
default_error_handler(ptr, x.reason); default_error_handler(ptr, x.reason);
} }
# ifndef CAF_NO_EXCEPTIONS #ifndef CAF_NO_EXCEPTIONS
error scheduled_actor::default_exception_handler(pointer ptr, error scheduled_actor::default_exception_handler(pointer ptr,
std::exception_ptr& x) { std::exception_ptr& x) {
CAF_ASSERT(x != nullptr); CAF_ASSERT(x != nullptr);
...@@ -98,8 +97,8 @@ error scheduled_actor::default_exception_handler(pointer ptr, ...@@ -98,8 +97,8 @@ error scheduled_actor::default_exception_handler(pointer ptr,
std::rethrow_exception(x); std::rethrow_exception(x);
} catch (const std::exception& e) { } catch (const std::exception& e) {
aout(ptr) << "*** unhandled exception: [id: " << ptr->id() aout(ptr) << "*** unhandled exception: [id: " << ptr->id()
<< ", name: " << ptr->name() << ", exception typeid: " << ", name: " << ptr->name()
<< typeid(e).name() << "]: " << e.what() << ", exception typeid: " << typeid(e).name() << "]: " << e.what()
<< std::endl; << std::endl;
} catch (...) { } catch (...) {
aout(ptr) << "*** unhandled exception: [id: " << ptr->id() aout(ptr) << "*** unhandled exception: [id: " << ptr->id()
...@@ -108,7 +107,7 @@ error scheduled_actor::default_exception_handler(pointer ptr, ...@@ -108,7 +107,7 @@ error scheduled_actor::default_exception_handler(pointer ptr,
} }
return sec::runtime_error; return sec::runtime_error;
} }
# endif // CAF_NO_EXCEPTIONS #endif // CAF_NO_EXCEPTIONS
// -- constructors and destructors --------------------------------------------- // -- constructors and destructors ---------------------------------------------
...@@ -121,10 +120,11 @@ scheduled_actor::scheduled_actor(actor_config& cfg) ...@@ -121,10 +120,11 @@ scheduled_actor::scheduled_actor(actor_config& cfg)
down_handler_(default_down_handler), down_handler_(default_down_handler),
exit_handler_(default_exit_handler), exit_handler_(default_exit_handler),
private_thread_(nullptr) private_thread_(nullptr)
# ifndef CAF_NO_EXCEPTIONS #ifndef CAF_NO_EXCEPTIONS
, exception_handler_(default_exception_handler) ,
# endif // CAF_NO_EXCEPTIONS exception_handler_(default_exception_handler)
{ #endif // CAF_NO_EXCEPTIONS
{
auto& sys_cfg = home_system().config(); auto& sys_cfg = home_system().config();
auto interval = sys_cfg.stream_tick_duration(); auto interval = sys_cfg.stream_tick_duration();
CAF_ASSERT(interval.count() > 0); CAF_ASSERT(interval.count() > 0);
...@@ -279,14 +279,17 @@ struct upstream_msg_visitor { ...@@ -279,14 +279,17 @@ struct upstream_msg_visitor {
} // namespace } // namespace
intrusive::task_result scheduled_actor::mailbox_visitor:: intrusive::task_result
operator()(size_t, upstream_queue&, mailbox_element& x) { scheduled_actor::mailbox_visitor::operator()(size_t, upstream_queue&,
mailbox_element& x) {
CAF_ASSERT(x.content().type_token() == make_type_token<upstream_msg>()); CAF_ASSERT(x.content().type_token() == make_type_token<upstream_msg>());
self->current_mailbox_element(&x); self->current_mailbox_element(&x);
CAF_LOG_RECEIVE_EVENT((&x)); CAF_LOG_RECEIVE_EVENT((&x));
CAF_BEFORE_PROCESSING(self, x);
auto& um = x.content().get_mutable_as<upstream_msg>(0); auto& um = x.content().get_mutable_as<upstream_msg>(0);
upstream_msg_visitor f{self, um}; upstream_msg_visitor f{self, um};
visit(f, um.content); visit(f, um.content);
CAF_AFTER_PROCESSING(self, invoke_message_result::consumed);
return ++handled_msgs < max_throughput ? intrusive::task_result::resume return ++handled_msgs < max_throughput ? intrusive::task_result::resume
: intrusive::task_result::stop_all; : intrusive::task_result::stop_all;
} }
...@@ -310,9 +313,9 @@ struct downstream_msg_visitor { ...@@ -310,9 +313,9 @@ struct downstream_msg_visitor {
inptr->handle(x); inptr->handle(x);
// The sender slot can be 0. This is the case for forced_close or // The sender slot can be 0. This is the case for forced_close or
// forced_drop messages from stream aborters. // forced_drop messages from stream aborters.
CAF_ASSERT(inptr->slots == dm.slots CAF_ASSERT(
|| (dm.slots.sender == 0 inptr->slots == dm.slots
&& dm.slots.receiver == inptr->slots.receiver)); || (dm.slots.sender == 0 && dm.slots.receiver == inptr->slots.receiver));
// TODO: replace with `if constexpr` when switching to C++17 // TODO: replace with `if constexpr` when switching to C++17
if (std::is_same<T, downstream_msg::close>::value if (std::is_same<T, downstream_msg::close>::value
|| std::is_same<T, downstream_msg::forced_close>::value) { || std::is_same<T, downstream_msg::forced_close>::value) {
...@@ -324,8 +327,7 @@ struct downstream_msg_visitor { ...@@ -324,8 +327,7 @@ struct downstream_msg_visitor {
mgr->stop(); mgr->stop();
} }
return intrusive::task_result::stop; return intrusive::task_result::stop;
} } else if (mgr->done()) {
else if (mgr->done()) {
CAF_LOG_DEBUG("path is done receiving and closes its manager"); CAF_LOG_DEBUG("path is done receiving and closes its manager");
selfptr->erase_stream_manager(mgr); selfptr->erase_stream_manager(mgr);
mgr->stop(); mgr->stop();
...@@ -337,17 +339,18 @@ struct downstream_msg_visitor { ...@@ -337,17 +339,18 @@ struct downstream_msg_visitor {
} // namespace } // namespace
intrusive::task_result scheduled_actor::mailbox_visitor:: intrusive::task_result scheduled_actor::mailbox_visitor::operator()(
operator()(size_t, downstream_queue& qs, stream_slot, size_t, downstream_queue& qs, stream_slot,
policy::downstream_messages::nested_queue_type& q, policy::downstream_messages::nested_queue_type& q, mailbox_element& x) {
mailbox_element& x) {
CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(handled_msgs)); CAF_LOG_TRACE(CAF_ARG(x) << CAF_ARG(handled_msgs));
self->current_mailbox_element(&x); self->current_mailbox_element(&x);
CAF_LOG_RECEIVE_EVENT((&x)); CAF_LOG_RECEIVE_EVENT((&x));
CAF_BEFORE_PROCESSING(self, x);
CAF_ASSERT(x.content().type_token() == make_type_token<downstream_msg>()); CAF_ASSERT(x.content().type_token() == make_type_token<downstream_msg>());
auto& dm = x.content().get_mutable_as<downstream_msg>(0); auto& dm = x.content().get_mutable_as<downstream_msg>(0);
downstream_msg_visitor f{self, qs, q, dm}; downstream_msg_visitor f{self, qs, q, dm};
auto res = visit(f, dm.content); auto res = visit(f, dm.content);
CAF_AFTER_PROCESSING(self, invoke_message_result::consumed);
return ++handled_msgs < max_throughput ? res return ++handled_msgs < max_throughput ? res
: intrusive::task_result::stop_all; : intrusive::task_result::stop_all;
} }
...@@ -359,8 +362,7 @@ scheduled_actor::mailbox_visitor::operator()(mailbox_element& x) { ...@@ -359,8 +362,7 @@ scheduled_actor::mailbox_visitor::operator()(mailbox_element& x) {
case activation_result::terminated: case activation_result::terminated:
return intrusive::task_result::stop; return intrusive::task_result::stop;
case activation_result::success: case activation_result::success:
return ++handled_msgs < max_throughput return ++handled_msgs < max_throughput ? intrusive::task_result::resume
? intrusive::task_result::resume
: intrusive::task_result::stop_all; : intrusive::task_result::stop_all;
case activation_result::skipped: case activation_result::skipped:
return intrusive::task_result::skip; return intrusive::task_result::skip;
...@@ -369,8 +371,8 @@ scheduled_actor::mailbox_visitor::operator()(mailbox_element& x) { ...@@ -369,8 +371,8 @@ scheduled_actor::mailbox_visitor::operator()(mailbox_element& x) {
} }
} }
resumable::resume_result resumable::resume_result scheduled_actor::resume(execution_unit* ctx,
scheduled_actor::resume(execution_unit* ctx, size_t max_throughput) { size_t max_throughput) {
CAF_PUSH_AID(id()); CAF_PUSH_AID(id());
CAF_LOG_TRACE(CAF_ARG(max_throughput)); CAF_LOG_TRACE(CAF_ARG(max_throughput));
if (!activate(ctx)) if (!activate(ctx))
...@@ -515,9 +517,7 @@ uint64_t scheduled_actor::set_stream_timeout(actor_clock::time_point x) { ...@@ -515,9 +517,7 @@ uint64_t scheduled_actor::set_stream_timeout(actor_clock::time_point x) {
mgrs.emplace_back(kvp.second); mgrs.emplace_back(kvp.second);
std::sort(mgrs.begin(), mgrs.end()); std::sort(mgrs.begin(), mgrs.end());
auto e = std::unique(mgrs.begin(), mgrs.end()); auto e = std::unique(mgrs.begin(), mgrs.end());
auto idle = [=](const stream_manager_ptr& y) { auto idle = [=](const stream_manager_ptr& y) { return y->idle(); };
return y->idle();
};
if (std::all_of(mgrs.begin(), e, idle)) { if (std::all_of(mgrs.begin(), e, idle)) {
CAF_LOG_DEBUG("suppress stream timeout"); CAF_LOG_DEBUG("suppress stream timeout");
return 0; return 0;
...@@ -616,7 +616,7 @@ scheduled_actor::categorize(mailbox_element& x) { ...@@ -616,7 +616,7 @@ scheduled_actor::categorize(mailbox_element& x) {
return message_category::internal; return message_category::internal;
} }
case make_type_token<open_stream_msg>(): { case make_type_token<open_stream_msg>(): {
return handle_open_stream_msg(x) != im_skipped return handle_open_stream_msg(x) != invoke_message_result::skipped
? message_category::internal ? message_category::internal
: message_category::skipped; : message_category::skipped;
} }
...@@ -629,31 +629,32 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) { ...@@ -629,31 +629,32 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
current_element_ = &x; current_element_ = &x;
CAF_LOG_RECEIVE_EVENT(current_element_); CAF_LOG_RECEIVE_EVENT(current_element_);
CAF_BEFORE_PROCESSING(this, x);
// Wrap the actual body for the function.
auto body = [this, &x] {
// Helper function for dispatching a message to a response handler. // Helper function for dispatching a message to a response handler.
using ptr_t = scheduled_actor*; using ptr_t = scheduled_actor*;
using fun_t = bool (*)(ptr_t, behavior&, mailbox_element&); using fun_t = bool (*)(ptr_t, behavior&, mailbox_element&);
auto ordinary_invoke = [](ptr_t, behavior& f, mailbox_element& in) -> bool { auto ordinary_invoke = [](ptr_t, behavior& f, mailbox_element& in) -> bool {
return f(in.content()) != none; return f(in.content()) != none;
}; };
auto select_invoke_fun = [&]() -> fun_t { auto select_invoke_fun = [&]() -> fun_t { return ordinary_invoke; };
return ordinary_invoke;
};
// Short-circuit awaited responses. // Short-circuit awaited responses.
if (!awaited_responses_.empty()) { if (!awaited_responses_.empty()) {
auto invoke = select_invoke_fun(); auto invoke = select_invoke_fun();
auto& pr = awaited_responses_.front(); auto& pr = awaited_responses_.front();
// skip all messages until we receive the currently awaited response // skip all messages until we receive the currently awaited response
if (x.mid != pr.first) if (x.mid != pr.first)
return im_skipped; return invoke_message_result::skipped;
auto f = std::move(pr.second); auto f = std::move(pr.second);
awaited_responses_.pop_front(); awaited_responses_.pop_front();
if (!invoke(this, f, x)) { if (!invoke(this, f, x)) {
// try again with error if first attempt failed // try again with error if first attempt failed
auto msg = make_message(make_error(sec::unexpected_response, auto msg = make_message(
x.move_content_to_message())); make_error(sec::unexpected_response, x.move_content_to_message()));
f(msg); f(msg);
} }
return im_success; return invoke_message_result::consumed;
} }
// Handle multiplexed responses. // Handle multiplexed responses.
if (x.mid.is_response()) { if (x.mid.is_response()) {
...@@ -661,24 +662,24 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) { ...@@ -661,24 +662,24 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) {
auto mrh = multiplexed_responses_.find(x.mid); auto mrh = multiplexed_responses_.find(x.mid);
// neither awaited nor multiplexed, probably an expired timeout // neither awaited nor multiplexed, probably an expired timeout
if (mrh == multiplexed_responses_.end()) if (mrh == multiplexed_responses_.end())
return im_dropped; return invoke_message_result::dropped;
auto bhvr = std::move(mrh->second); auto bhvr = std::move(mrh->second);
multiplexed_responses_.erase(mrh); multiplexed_responses_.erase(mrh);
if (!invoke(this, bhvr, x)) { if (!invoke(this, bhvr, x)) {
// try again with error if first attempt failed // try again with error if first attempt failed
auto msg = make_message(make_error(sec::unexpected_response, auto msg = make_message(
x.move_content_to_message())); make_error(sec::unexpected_response, x.move_content_to_message()));
bhvr(msg); bhvr(msg);
} }
return im_success; return invoke_message_result::consumed;
} }
// Dispatch on the content of x. // Dispatch on the content of x.
switch (categorize(x)) { switch (categorize(x)) {
case message_category::skipped: case message_category::skipped:
return im_skipped; return invoke_message_result::skipped;
case message_category::internal: case message_category::internal:
CAF_LOG_DEBUG("handled system message"); CAF_LOG_DEBUG("handled system message");
return im_success; return invoke_message_result::consumed;
case message_category::ordinary: { case message_category::ordinary: {
detail::default_invoke_result_visitor<scheduled_actor> visitor{this}; detail::default_invoke_result_visitor<scheduled_actor> visitor{this};
bool skipped = false; bool skipped = false;
...@@ -705,7 +706,8 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) { ...@@ -705,7 +706,8 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) {
}; };
if (bhvr_stack_.empty()) { if (bhvr_stack_.empty()) {
call_default_handler(); call_default_handler();
return !skipped ? im_success : im_skipped; return !skipped ? invoke_message_result::consumed
: invoke_message_result::skipped;
} }
auto& bhvr = bhvr_stack_.back(); auto& bhvr = bhvr_stack_.back();
switch (bhvr(visitor, x.content())) { switch (bhvr(visitor, x.content())) {
...@@ -717,11 +719,18 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) { ...@@ -717,11 +719,18 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) {
case match_case::no_match: case match_case::no_match:
call_default_handler(); call_default_handler();
} }
return !skipped ? im_success : im_skipped; return !skipped ? invoke_message_result::consumed
: invoke_message_result::skipped;
} }
} }
// Unreachable. // Unreachable.
CAF_CRITICAL("invalid message type"); CAF_CRITICAL("invalid message type");
};
// Post-process the returned value from the function body.
auto result = body();
CAF_AFTER_PROCESSING(this, result);
CAF_LOG_SKIP_OR_FINALIZE_EVENT(result);
return result;
} }
/// Tries to consume `x`. /// Tries to consume `x`.
...@@ -729,7 +738,7 @@ void scheduled_actor::consume(mailbox_element_ptr x) { ...@@ -729,7 +738,7 @@ void scheduled_actor::consume(mailbox_element_ptr x) {
switch (consume(*x)) { switch (consume(*x)) {
default: default:
break; break;
case im_skipped: case invoke_message_result::skipped:
push_to_cache(std::move(x)); push_to_cache(std::move(x));
} }
} }
...@@ -743,9 +752,9 @@ bool scheduled_actor::activate(execution_unit* ctx) { ...@@ -743,9 +752,9 @@ bool scheduled_actor::activate(execution_unit* ctx) {
CAF_LOG_ERROR("activate called on a terminated actor"); CAF_LOG_ERROR("activate called on a terminated actor");
return false; return false;
} }
# ifndef CAF_NO_EXCEPTIONS #ifndef CAF_NO_EXCEPTIONS
try { try {
# endif // CAF_NO_EXCEPTIONS #endif // CAF_NO_EXCEPTIONS
if (!getf(is_initialized_flag)) { if (!getf(is_initialized_flag)) {
initialize(); initialize();
if (finalize()) { if (finalize()) {
...@@ -754,21 +763,20 @@ bool scheduled_actor::activate(execution_unit* ctx) { ...@@ -754,21 +763,20 @@ bool scheduled_actor::activate(execution_unit* ctx) {
} }
CAF_LOG_DEBUG("initialized actor:" << CAF_ARG(name())); CAF_LOG_DEBUG("initialized actor:" << CAF_ARG(name()));
} }
# ifndef CAF_NO_EXCEPTIONS #ifndef CAF_NO_EXCEPTIONS
} } catch (...) {
catch (...) {
CAF_LOG_ERROR("actor died during initialization"); CAF_LOG_ERROR("actor died during initialization");
auto eptr = std::current_exception(); auto eptr = std::current_exception();
quit(call_handler(exception_handler_, this, eptr)); quit(call_handler(exception_handler_, this, eptr));
finalize(); finalize();
return false; return false;
} }
# endif // CAF_NO_EXCEPTIONS #endif // CAF_NO_EXCEPTIONS
return true; return true;
} }
auto scheduled_actor::activate(execution_unit* ctx, mailbox_element& x) auto scheduled_actor::activate(execution_unit* ctx, mailbox_element& x)
-> activation_result { -> activation_result {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
if (!activate(ctx)) if (!activate(ctx))
return activation_result::terminated; return activation_result::terminated;
...@@ -780,38 +788,36 @@ auto scheduled_actor::activate(execution_unit* ctx, mailbox_element& x) ...@@ -780,38 +788,36 @@ auto scheduled_actor::activate(execution_unit* ctx, mailbox_element& x)
auto scheduled_actor::reactivate(mailbox_element& x) -> activation_result { auto scheduled_actor::reactivate(mailbox_element& x) -> activation_result {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
# ifndef CAF_NO_EXCEPTIONS #ifndef CAF_NO_EXCEPTIONS
try { try {
# endif // CAF_NO_EXCEPTIONS #endif // CAF_NO_EXCEPTIONS
switch (consume(x)) { switch (consume(x)) {
case im_dropped: case invoke_message_result::dropped:
return activation_result::dropped; return activation_result::dropped;
case im_success: case invoke_message_result::consumed:
bhvr_stack_.cleanup(); bhvr_stack_.cleanup();
if (finalize()) { if (finalize()) {
CAF_LOG_DEBUG("actor finalized"); CAF_LOG_DEBUG("actor finalized");
return activation_result::terminated; return activation_result::terminated;
} }
return activation_result::success; return activation_result::success;
case im_skipped: case invoke_message_result::skipped:
return activation_result::skipped; return activation_result::skipped;
} }
# ifndef CAF_NO_EXCEPTIONS #ifndef CAF_NO_EXCEPTIONS
} } catch (std::exception& e) {
catch (std::exception& e) {
CAF_LOG_INFO("actor died because of an exception, what: " << e.what()); CAF_LOG_INFO("actor died because of an exception, what: " << e.what());
static_cast<void>(e); // keep compiler happy when not logging static_cast<void>(e); // keep compiler happy when not logging
auto eptr = std::current_exception(); auto eptr = std::current_exception();
quit(call_handler(exception_handler_, this, eptr)); quit(call_handler(exception_handler_, this, eptr));
} } catch (...) {
catch (...) {
CAF_LOG_INFO("actor died because of an unknown exception"); CAF_LOG_INFO("actor died because of an unknown exception");
auto eptr = std::current_exception(); auto eptr = std::current_exception();
quit(call_handler(exception_handler_, this, eptr)); quit(call_handler(exception_handler_, this, eptr));
} }
finalize(); finalize();
return activation_result::terminated; return activation_result::terminated;
# endif // CAF_NO_EXCEPTIONS #endif // CAF_NO_EXCEPTIONS
} }
// -- behavior management ---------------------------------------------------- // -- behavior management ----------------------------------------------------
...@@ -1103,14 +1109,13 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) { ...@@ -1103,14 +1109,13 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) {
auto sres = call_handler(default_handler_, this, x); auto sres = call_handler(default_handler_, this, x);
switch (sres.flag) { switch (sres.flag) {
default: default:
CAF_LOG_DEBUG("default handler was called for open_stream_msg:" CAF_LOG_DEBUG(
<< osm.msg); "default handler was called for open_stream_msg:" << osm.msg);
fail(sec::stream_init_failed, "dropped open_stream_msg (no match)"); fail(sec::stream_init_failed, "dropped open_stream_msg (no match)");
return im_dropped; return invoke_message_result::dropped;
case rt_skip: case rt_skip:
CAF_LOG_DEBUG("default handler skipped open_stream_msg:" CAF_LOG_DEBUG("default handler skipped open_stream_msg:" << osm.msg);
<< osm.msg); return invoke_message_result::skipped;
return im_skipped;
} }
}; };
// Invoke behavior and dispatch on the result. // Invoke behavior and dispatch on the result.
...@@ -1123,11 +1128,11 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) { ...@@ -1123,11 +1128,11 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) {
CAF_LOG_DEBUG("no match in behavior, fall back to default handler"); CAF_LOG_DEBUG("no match in behavior, fall back to default handler");
return fallback(); return fallback();
case match_case::result::match: { case match_case::result::match: {
return im_success; return invoke_message_result::consumed;
} }
default: default:
CAF_LOG_DEBUG("behavior skipped open_stream_msg:" << osm.msg); CAF_LOG_DEBUG("behavior skipped open_stream_msg:" << osm.msg);
return im_skipped; // nop return invoke_message_result::skipped; // 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. *
******************************************************************************/
#define CAF_SUITE actor_profiler
#include "caf/actor_profiler.hpp"
#include "caf/test/dsl.hpp"
#include "caf/config.hpp"
#ifdef CAF_ENABLE_ACTOR_PROFILER
using namespace caf;
namespace {
using string_list = std::vector<std::string>;
struct recorder : actor_profiler {
void add_actor(const local_actor& self, const local_actor* parent) {
log.emplace_back("new: ");
auto& str = log.back();
str += self.name();
if (parent != nullptr) {
str += ", parent: ";
str += parent->name();
}
}
void remove_actor(const local_actor& self) {
log.emplace_back("delete: ");
log.back() += self.name();
}
void before_processing(const local_actor& self,
const mailbox_element& element) {
log.emplace_back(self.name());
auto& str = log.back();
str += " got: ";
str += to_string(element.content());
}
void after_processing(const local_actor& self, invoke_message_result result) {
log.emplace_back(self.name());
auto& str = log.back();
str += " ";
str += to_string(result);
str += " the message";
}
string_list log;
};
actor_system_config& init(actor_system_config& cfg, recorder& rec) {
test_coordinator_fixture<>::init_config(cfg);
cfg.profiler = &rec;
return cfg;
}
struct fixture {
using scheduler_type = caf::scheduler::test_coordinator;
fixture()
: sys(init(cfg, rec)),
sched(dynamic_cast<scheduler_type&>(sys.scheduler())) {
// nop
}
void run() {
sched.run();
}
recorder rec;
actor_system_config cfg;
actor_system sys;
scheduler_type& sched;
};
struct foo_state {
const char* name = "foo";
};
struct bar_state {
const char* name = "bar";
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(actor_profiler_tests, fixture)
CAF_TEST(record actor construction) {
CAF_MESSAGE("fully initialize CAF, ignore system-internal actors");
run();
rec.log.clear();
CAF_MESSAGE("spawn a foo and a bar");
auto bar = [](stateful_actor<bar_state>*) {};
auto foo = [bar](stateful_actor<foo_state>* self) { self->spawn(bar); };
auto foo_actor = sys.spawn(foo);
run();
foo_actor = nullptr;
CAF_CHECK_EQUAL(string_list({
"new: foo",
"new: bar, parent: foo",
"delete: bar",
"delete: foo",
}),
rec.log);
}
CAF_TEST(record actor messaging) {
CAF_MESSAGE("fully initialize CAF, ignore system-internal actors");
run();
rec.log.clear();
CAF_MESSAGE("spawn a foo and a bar");
auto bar = [](stateful_actor<bar_state>*) -> behavior {
return {
[](const std::string& str) {
CAF_CHECK_EQUAL(str, "hello bar");
return "hello foo";
},
};
};
auto foo = [bar](stateful_actor<foo_state>* self) -> behavior {
auto b = self->spawn(bar);
self->send(b, "hello bar");
return {
[](const std::string& str) { CAF_CHECK_EQUAL(str, "hello foo"); },
};
};
sys.spawn(foo);
run();
CAF_CHECK_EQUAL(string_list({
"new: foo",
"new: bar, parent: foo",
"bar got: (\"hello bar\")",
"bar consumed the message",
"foo got: (\"hello foo\")",
"delete: bar",
"foo consumed the message",
"delete: foo",
}),
rec.log);
}
CAF_TEST_FIXTURE_SCOPE_END()
#endif // CAF_ENABLE_ACTOR_PROFILER
...@@ -60,13 +60,13 @@ void manager::detach(execution_unit*, bool invoke_disconnect_message) { ...@@ -60,13 +60,13 @@ void manager::detach(execution_unit*, bool invoke_disconnect_message) {
auto mptr = make_mailbox_element(nullptr, make_message_id(), {}, auto mptr = make_mailbox_element(nullptr, make_message_id(), {},
detach_message()); detach_message());
switch (raw_ptr->consume(*mptr)) { switch (raw_ptr->consume(*mptr)) {
case im_success: case invoke_message_result::consumed:
raw_ptr->finalize(); raw_ptr->finalize();
break; break;
case im_skipped: case invoke_message_result::skipped:
raw_ptr->push_to_cache(std::move(mptr)); raw_ptr->push_to_cache(std::move(mptr));
break; break;
case im_dropped: case invoke_message_result::dropped:
CAF_LOG_INFO("broker dropped disconnect message"); CAF_LOG_INFO("broker dropped disconnect message");
break; break;
} }
......
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