Commit de53c367 authored by Dominik Charousset's avatar Dominik Charousset

Re-implement rudimentary source->sink streaming

parent b3bd97ad
...@@ -416,6 +416,8 @@ public: ...@@ -416,6 +416,8 @@ public:
bool cleanup(error&& fail_state, execution_unit* host) override; bool cleanup(error&& fail_state, execution_unit* host) override;
sec build_pipeline(stream_manager_ptr);
/// @endcond /// @endcond
private: private:
......
...@@ -88,9 +88,9 @@ struct dmi<optional<Y> (Xs...)> : dmi<Y (Xs...)> {}; ...@@ -88,9 +88,9 @@ struct dmi<optional<Y> (Xs...)> : dmi<Y (Xs...)> {};
template <class Y, class... Xs> template <class Y, class... Xs>
struct dmi<expected<Y> (Xs...)> : dmi<Y (Xs...)> {}; struct dmi<expected<Y> (Xs...)> : dmi<Y (Xs...)> {};
// case #5: function returning an annotated_stream<> // case #5: function returning an output_stream<>
template <class Y, class... Ys, class... Xs> template <class Y, class... Ys, class... Xs>
struct dmi<annotated_stream<Y, Ys...> (Xs...)> : dmi<Y (Xs...)> { struct dmi<output_stream<Y, Ys...> (Xs...)> : dmi<Y (Xs...)> {
using type = using type =
typed_mpi<type_list<typename param_decay<Xs>::type...>, typed_mpi<type_list<typename param_decay<Xs>::type...>,
output_tuple<stream<Y>, strip_and_convert_t<Ys>...>>; output_tuple<stream<Y>, strip_and_convert_t<Ys>...>>;
......
...@@ -56,6 +56,11 @@ public: ...@@ -56,6 +56,11 @@ public:
delegate(x); delegate(x);
} }
void operator()(stream_slot, stream_manager_ptr& ptr) override {
// TODO: error handling
self_->build_pipeline(std::move(ptr));
}
private: private:
void deliver(response_promise& rp, error& x) { void deliver(response_promise& rp, error& x) {
CAF_LOG_DEBUG("report error back to requesting actor"); CAF_LOG_DEBUG("report error back to requesting actor");
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "caf/expected.hpp" #include "caf/expected.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/make_message.hpp" #include "caf/make_message.hpp"
#include "caf/stream_result.hpp"
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
...@@ -37,6 +38,8 @@ ...@@ -37,6 +38,8 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
/// Inspects the result of message handlers and triggers type-depended actions
/// such as generating result messages.
class invoke_result_visitor { class invoke_result_visitor {
public: public:
virtual ~invoke_result_visitor(); virtual ~invoke_result_visitor();
...@@ -45,26 +48,36 @@ public: ...@@ -45,26 +48,36 @@ public:
// nop // nop
} }
// severs as catch-all case for all values producing // -- virtual handlers -------------------------------------------------------
// no result such as response_promise
/// Called whenever no result messages gets produced, e.g., when returning a
/// `response_promise`.
virtual void operator()() = 0; virtual void operator()() = 0;
// called if the message handler explicitly returned an error /// Called if the message handler returned an error.
virtual void operator()(error&) = 0; virtual void operator()(error&) = 0;
// called if the message handler returned any "ordinary" value /// Called if the message handler returned any "ordinary" value.
virtual void operator()(message&) = 0; virtual void operator()(message&) = 0;
// called if the message handler returns explictly none or unit /// Called if the message handler returned a "stream<T>",
/// `output_stream<T, ...>`, or `stream_result<T>`.
virtual void operator()(stream_slot, stream_manager_ptr&) = 0;
/// Called if the message handler returns "nothing", for example a
/// default-constructed `optional<T>`.
virtual void operator()(const none_t&) = 0; virtual void operator()(const none_t&) = 0;
// map unit to an empty message // -- on-the-fly type conversions --------------------------------------------
/// Called if the message handler returns `void` or `unit_t`.
inline void operator()(const unit_t&) { inline void operator()(const unit_t&) {
message empty_msg; message empty_msg;
(*this)(empty_msg); (*this)(empty_msg);
} }
// unwrap optionals /// Unwraps an `optional<T>` by recursively calling the visitor with either
/// `none_t` or `T`.
template <class T> template <class T>
void operator()(optional<T>& x) { void operator()(optional<T>& x) {
if (x) if (x)
...@@ -73,7 +86,8 @@ public: ...@@ -73,7 +86,8 @@ public:
(*this)(none); (*this)(none);
} }
// unwrap expecteds /// Unwraps an `expected<T>` by recursively calling the visitor with either
/// `error` or `T`.
template <class T> template <class T>
void operator()(expected<T>& x) { void operator()(expected<T>& x) {
if (x) if (x)
...@@ -82,71 +96,88 @@ public: ...@@ -82,71 +96,88 @@ public:
(*this)(x.error()); (*this)(x.error());
} }
// convert values to messages /// Wraps arbitrary values into a `message` and calls the visitor recursively.
template <class... Ts> template <class... Ts>
void operator()(Ts&... xs) { void operator()(Ts&... xs) {
auto tmp = make_message(std::move(xs)...); auto tmp = make_message(std::move(xs)...);
(*this)(tmp); (*this)(tmp);
} }
// unwrap tuples /// Wraps the tuple into a `message` and calls the visitor recursively with
/// its contents.
template <class... Ts> template <class... Ts>
void operator()(std::tuple<Ts...>& xs) { void operator()(std::tuple<Ts...>& xs) {
apply_args(*this, get_indices(xs), xs); apply_args(*this, get_indices(xs), xs);
} }
// disambiguations /// Disambiguates the variadic `operator<Ts...>()`.
inline void operator()(none_t& x) { inline void operator()(none_t& x) {
(*this)(const_cast<const none_t&>(x)); (*this)(const_cast<const none_t&>(x));
} }
/// Disambiguates the variadic `operator<Ts...>()`.
inline void operator()(unit_t& x) { inline void operator()(unit_t& x) {
(*this)(const_cast<const unit_t&>(x)); (*this)(const_cast<const unit_t&>(x));
} }
// special purpose handler that don't procude results // -- special-purpose handlers that don't procude results --------------------
/// Calls `(*this)()`.
inline void operator()(response_promise&) { inline void operator()(response_promise&) {
(*this)(); (*this)();
} }
/// Calls `(*this)()`.
template <class... Ts> template <class... Ts>
void operator()(typed_response_promise<Ts...>&) { void operator()(typed_response_promise<Ts...>&) {
(*this)(); (*this)();
} }
/// Calls `(*this)()`.
template <class... Ts> template <class... Ts>
void operator()(delegated<Ts...>&) { void operator()(delegated<Ts...>&) {
(*this)(); (*this)();
} }
/// Calls `(*this)(x.ptr)`.
template <class T> template <class T>
void operator()(stream<T>&) { void operator()(stream<T>& x) {
(*this)(); (*this)(x.slot(), x.ptr());
}
/// Calls `(*this)(x.ptr)`.
template <class T, class... Us>
void operator()(output_stream<T, Us...>& x) {
(*this)(x.slot(), x.ptr());
} }
// visit API - returns true if T was visited, false if T was skipped /// Calls `(*this)(x.ptr)`.
template <class T>
void operator()(stream_result<T>& x) {
(*this)(x.slot(), x.ptr());
}
// -- visit API: return true if T was visited, false if T was skipped --------
/// Delegates `x` to the appropriate handler and returns `true`.
template <class T> template <class T>
bool visit(T& x) { bool visit(T& x) {
(*this)(x); (*this)(x);
return true; return true;
} }
/// Returns `false`.
inline bool visit(skip_t&) { inline bool visit(skip_t&) {
return false; return false;
} }
/// Returns `false`.
inline bool visit(const skip_t&) { inline bool visit(const skip_t&) {
return false; return false;
} }
template <class T> /// Returns `false` if `x != none`, otherwise calls the void handler and
bool visit(stream<T>&) { /// returns `true`..
return true;
}
inline bool visit(optional<skip_t>& x) { inline bool visit(optional<skip_t>& x) {
if (x) if (x)
return false; return false;
...@@ -154,6 +185,7 @@ public: ...@@ -154,6 +185,7 @@ public:
return true; return true;
} }
/// Dispatches on the runtime-type of `x`.
template <class... Ts> template <class... Ts>
bool visit(result<Ts...>& x) { bool visit(result<Ts...>& x) {
switch (x.flag) { switch (x.flag) {
......
...@@ -64,6 +64,7 @@ public: ...@@ -64,6 +64,7 @@ public:
if (x.xs.match_elements<vec_type>()) { if (x.xs.match_elements<vec_type>()) {
auto& xs = x.xs.get_mutable_as<vec_type>(0); auto& xs = x.xs.get_mutable_as<vec_type>(0);
driver_.process(std::move(xs)); driver_.process(std::move(xs));
return;
} }
CAF_LOG_ERROR("received unexpected batch type (dropped)"); CAF_LOG_ERROR("received unexpected batch type (dropped)");
} }
......
...@@ -43,7 +43,7 @@ public: ...@@ -43,7 +43,7 @@ public:
using stream_type = stream<output_type>; using stream_type = stream<output_type>;
using annotated_stream_type = typename super::annotated_stream_type; using output_stream_type = typename super::output_stream_type;
using tuple_type = std::tuple<Ts...>; using tuple_type = std::tuple<Ts...>;
......
...@@ -83,6 +83,10 @@ public: ...@@ -83,6 +83,10 @@ public:
return hint != out_.capacity(); return hint != out_.capacity();
} }
message make_handshake() const override {
return make_message_from_tuple(driver_.make_handshake());
}
private: private:
bool at_end_; bool at_end_;
Driver driver_; Driver driver_;
......
...@@ -47,7 +47,7 @@ public: ...@@ -47,7 +47,7 @@ public:
using stream_type = stream<output_type>; using stream_type = stream<output_type>;
using annotated_stream_type = typename super::annotated_stream_type; using output_stream_type = typename super::output_stream_type;
using tuple_type = std::tuple<Ts...>; using tuple_type = std::tuple<Ts...>;
......
...@@ -29,16 +29,19 @@ ...@@ -29,16 +29,19 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
/// Drains a mailbox and sends an error message to each unhandled request.
struct sync_request_bouncer { struct sync_request_bouncer {
error rsn; error rsn;
explicit sync_request_bouncer(error r); explicit sync_request_bouncer(error r);
void operator()(const strong_actor_ptr& sender, const message_id& mid) const; void operator()(const strong_actor_ptr& sender, const message_id& mid) const;
void operator()(const mailbox_element& e) const; void operator()(const mailbox_element& e) const;
template <class Key, class Queue> /// Unwrap WDRR queues. Nesting WDRR queues results in a Key/Queue prefix for
/// each layer of nesting.
template <class Key, class Queue, class... Ts>
intrusive::task_result operator()(const Key&, const Queue&, intrusive::task_result operator()(const Key&, const Queue&,
const mailbox_element& x) const { const Ts&... xs) const {
(*this)(x); (*this)(xs...);
return intrusive::task_result::resume; return intrusive::task_result::resume;
} }
}; };
......
...@@ -62,6 +62,11 @@ public: ...@@ -62,6 +62,11 @@ public:
/// Configures the time interval per tick. /// Configures the time interval per tick.
void interval(duration_type x); void interval(duration_type x);
/// Returns the time interval per tick.
inline duration_type interval() const {
return interval_;
}
/// Advances time and calls `consumer` for each emitted tick. /// Advances time and calls `consumer` for each emitted tick.
template <class F> template <class F>
void update(time_point now, F& consumer) { void update(time_point now, F& consumer) {
......
...@@ -57,7 +57,7 @@ template <class...> class typed_event_based_actor; ...@@ -57,7 +57,7 @@ template <class...> class typed_event_based_actor;
// -- variadic templates with 1 fixed argument --------------------------------- // -- variadic templates with 1 fixed argument ---------------------------------
template <class, class...> class annotated_stream; template <class, class...> class output_stream;
// -- classes ------------------------------------------------------------------ // -- classes ------------------------------------------------------------------
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "caf/stream_manager.hpp" #include "caf/stream_manager.hpp"
#include "caf/stream_priority.hpp" #include "caf/stream_priority.hpp"
#include "caf/stream_slot.hpp" #include "caf/stream_slot.hpp"
#include "caf/timestamp.hpp"
#include "caf/upstream_msg.hpp" #include "caf/upstream_msg.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
...@@ -81,7 +82,7 @@ public: ...@@ -81,7 +82,7 @@ public:
struct stats_t { struct stats_t {
struct measurement { struct measurement {
long batch_size; long batch_size;
std::chrono::nanoseconds calculation_time; timespan calculation_time;
}; };
struct calculation_result { struct calculation_result {
...@@ -99,8 +100,7 @@ public: ...@@ -99,8 +100,7 @@ public:
/// Returns the maximum number of items this actor could handle for given /// Returns the maximum number of items this actor could handle for given
/// cycle length with a minimum of 1. /// cycle length with a minimum of 1.
calculation_result calculate(std::chrono::nanoseconds cycle, calculation_result calculate(timespan cycle, timespan desired_complexity);
std::chrono::nanoseconds desired_complexity);
/// Stores a new measurement in the ring buffer. /// Stores a new measurement in the ring buffer.
void store(measurement x); void store(measurement x);
...@@ -129,9 +129,8 @@ public: ...@@ -129,9 +129,8 @@ public:
/// @param self Points to the parent actor. /// @param self Points to the parent actor.
/// @param queued_items Counts the accumulated size of all batches that are /// @param queued_items Counts the accumulated size of all batches that are
/// currently waiting in the mailbox. /// currently waiting in the mailbox.
void emit_ack_batch(local_actor* self, long queued_items, void emit_ack_batch(local_actor* self, long queued_items, timespan cycle,
std::chrono::nanoseconds cycle, timespan desired_batch_complexity);
std::chrono::nanoseconds desired_batch_complexity);
/// Sends a `stream_msg::close` on this path. /// Sends a `stream_msg::close` on this path.
void emit_regular_shutdown(local_actor* self); void emit_regular_shutdown(local_actor* self);
......
...@@ -97,6 +97,8 @@ public: ...@@ -97,6 +97,8 @@ public:
bool remove_backlink(abstract_actor* x) override; bool remove_backlink(abstract_actor* x) override;
error fail_state() const;
/// @endcond /// @endcond
protected: protected:
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <string> #include <string>
#include "caf/illegal_message_element.hpp" #include "caf/illegal_message_element.hpp"
#include "caf/stream.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/type_pair.hpp" #include "caf/detail/type_pair.hpp"
...@@ -36,9 +37,6 @@ std::string replies_to_type_name(size_t input_size, ...@@ -36,9 +37,6 @@ std::string replies_to_type_name(size_t input_size,
const std::string* output_opt1); const std::string* output_opt1);
/// @endcond /// @endcond
template <class...>
struct output_stream {};
template <class...> template <class...>
struct output_tuple {}; struct output_tuple {};
...@@ -70,8 +68,9 @@ struct replies_to { ...@@ -70,8 +68,9 @@ struct replies_to {
using with = typed_mpi<detail::type_list<Is...>, output_tuple<Os...>>; using with = typed_mpi<detail::type_list<Is...>, output_tuple<Os...>>;
/// @private /// @private
template <class... Os> template <class O, class... Os>
using with_stream = typed_mpi<detail::type_list<Is...>, output_stream<Os...>>; using with_stream = typed_mpi<detail::type_list<Is...>,
output_stream<O, Os...>>;
}; };
template <class... Is> template <class... Is>
......
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include "caf/policy/urgent_messages.hpp" #include "caf/policy/urgent_messages.hpp"
#include "caf/detail/behavior_stack.hpp" #include "caf/detail/behavior_stack.hpp"
#include "caf/detail/tick_emitter.hpp"
#include "caf/detail/stream_sink_driver_impl.hpp" #include "caf/detail/stream_sink_driver_impl.hpp"
#include "caf/detail/stream_sink_impl.hpp" #include "caf/detail/stream_sink_impl.hpp"
#include "caf/detail/stream_source_driver_impl.hpp" #include "caf/detail/stream_source_driver_impl.hpp"
...@@ -104,10 +105,6 @@ public: ...@@ -104,10 +105,6 @@ public:
/// Categorizes incoming messages. /// Categorizes incoming messages.
enum class message_category { enum class message_category {
/// Denotes an expired and thus obsolete timeout.
expired_timeout,
/// Triggers the currently active timeout.
timeout,
/// Triggers the current behavior. /// Triggers the current behavior.
ordinary, ordinary,
/// Triggers handlers for system messages such as `exit_msg` or `down_msg`. /// Triggers handlers for system messages such as `exit_msg` or `down_msg`.
...@@ -141,7 +138,8 @@ public: ...@@ -141,7 +138,8 @@ public:
using upstream_queue = intrusive::drr_queue<policy::upstream_messages>; using upstream_queue = intrusive::drr_queue<policy::upstream_messages>;
/// Stores downstream messages. /// Stores downstream messages.
using downstream_queue = intrusive::drr_queue<policy::downstream_messages>; using downstream_queue =
intrusive::wdrr_dynamic_multiplexed_queue<policy::downstream_messages>;
/// Configures the FIFO inbox with four nested queues: /// Configures the FIFO inbox with four nested queues:
/// ///
...@@ -166,6 +164,10 @@ public: ...@@ -166,6 +164,10 @@ public:
static constexpr size_t default_queue_index = 0; static constexpr size_t default_queue_index = 0;
static constexpr size_t upstream_queue_index = 1;
static constexpr size_t downstream_queue_index = 2;
static constexpr size_t urgent_queue_index = 3; static constexpr size_t urgent_queue_index = 3;
}; };
...@@ -214,8 +216,10 @@ public: ...@@ -214,8 +216,10 @@ public:
mailbox_element&); mailbox_element&);
/// Consumes downstream messages. /// Consumes downstream messages.
intrusive::task_result operator()(size_t, downstream_queue&, intrusive::task_result
mailbox_element&); operator()(size_t, downstream_queue&, stream_slot slot,
policy::downstream_messages::nested_queue_type&,
mailbox_element&);
// Dispatches asynchronous messages with high and normal priority to the // Dispatches asynchronous messages with high and normal priority to the
// same handler. // same handler.
...@@ -403,20 +407,17 @@ public: ...@@ -403,20 +407,17 @@ public:
template <class Driver, template <class Driver,
class Scatterer = broadcast_scatterer<typename Driver::output_type>, class Scatterer = broadcast_scatterer<typename Driver::output_type>,
class... Ts> class... Ts>
typename Driver::annotated_stream_type make_source(Ts&&... xs) { typename Driver::output_stream_type make_source(Ts&&... xs) {
auto slot = next_slot();
auto ptr = detail::make_stream_source<Driver, Scatterer>( auto ptr = detail::make_stream_source<Driver, Scatterer>(
this, std::forward<Ts>(xs)...); this, std::forward<Ts>(xs)...);
ptr->generate_messages(); return {0, std::move(ptr)};
pending_stream_managers_.emplace(slot, ptr);
return {slot, std::move(ptr)};
} }
template <class... Ts, class Init, class Pull, class Done, template <class... Ts, class Init, class Pull, class Done,
class Scatterer = class Scatterer =
broadcast_scatterer<typename stream_source_trait_t<Pull>::output>, broadcast_scatterer<typename stream_source_trait_t<Pull>::output>,
class Trait = stream_source_trait_t<Pull>> class Trait = stream_source_trait_t<Pull>>
annotated_stream<typename Trait::output, detail::decay_t<Ts>...> output_stream<typename Trait::output, detail::decay_t<Ts>...>
make_source(std::tuple<Ts...> xs, Init init, Pull pull, Done done, make_source(std::tuple<Ts...> xs, Init init, Pull pull, Done done,
policy::arg<Scatterer> = {}) { policy::arg<Scatterer> = {}) {
using tuple_type = std::tuple<detail::decay_t<Ts>...>; using tuple_type = std::tuple<detail::decay_t<Ts>...>;
...@@ -432,7 +433,10 @@ public: ...@@ -432,7 +433,10 @@ public:
auto slot = next_slot(); auto slot = next_slot();
stream_slots id{in.slot(), slot}; stream_slots id{in.slot(), slot};
auto ptr = detail::make_stream_sink<Driver>(this, std::forward<Ts>(xs)...); auto ptr = detail::make_stream_sink<Driver>(this, std::forward<Ts>(xs)...);
stream_managers_.emplace(id, ptr); if (!add_stream_manager(id, ptr)) {
CAF_LOG_WARNING("unable to add a stream manager for a sink");
return {0, nullptr};
}
return {slot, std::move(ptr)}; return {slot, std::move(ptr)};
} }
...@@ -460,7 +464,7 @@ public: ...@@ -460,7 +464,7 @@ public:
`stream_manager`. template <class Handle, class... Ts, class Init, class `stream_manager`. template <class Handle, class... Ts, class Init, class
Getter, class ClosedPredicate, class ResHandler, class Scatterer = Getter, class ClosedPredicate, class ResHandler, class Scatterer =
broadcast_scatterer< typename stream_source_trait_t<Getter>::output>> broadcast_scatterer< typename stream_source_trait_t<Getter>::output>>
annotated_stream<typename stream_source_trait_t<Getter>::output, Ts...> output_stream<typename stream_source_trait_t<Getter>::output, Ts...>
make_source(const Handle& dest, std::tuple<Ts...> xs, Init init, make_source(const Handle& dest, std::tuple<Ts...> xs, Init init,
Getter getter, ClosedPredicate pred, ResHandler res_handler, Getter getter, ClosedPredicate pred, ResHandler res_handler,
policy::arg<Scatterer> scatterer_type = {}) { policy::arg<Scatterer> scatterer_type = {}) {
...@@ -531,7 +535,7 @@ public: ...@@ -531,7 +535,7 @@ public:
/// @returns A stream object with a pointer to the generated /// @returns A stream object with a pointer to the generated
`stream_manager`. template <class Init, class... Ts, class Getter, class `stream_manager`. template <class Init, class... Ts, class Getter, class
ClosedPredicate, class Scatterer = broadcast_scatterer< typename ClosedPredicate, class Scatterer = broadcast_scatterer< typename
stream_source_trait_t<Getter>::output>> annotated_stream<typename stream_source_trait_t<Getter>::output>> output_stream<typename
stream_source_trait_t<Getter>::output, Ts...> make_source(std::tuple<Ts...> stream_source_trait_t<Getter>::output, Ts...> make_source(std::tuple<Ts...>
xs, Init init, Getter getter, ClosedPredicate pred, policy::arg<Scatterer> xs, Init init, Getter getter, ClosedPredicate pred, policy::arg<Scatterer>
scatterer_type = {}) { CAF_IGNORE_UNUSED(scatterer_type); using type = scatterer_type = {}) { CAF_IGNORE_UNUSED(scatterer_type); using type =
...@@ -598,7 +602,7 @@ public: ...@@ -598,7 +602,7 @@ public:
`stream_manager`. template <class In, class... Ts, class Init, class Fun, `stream_manager`. template <class In, class... Ts, class Init, class Fun,
class Cleanup, class Gatherer = random_gatherer, class Scatterer = class Cleanup, class Gatherer = random_gatherer, class Scatterer =
broadcast_scatterer<typename stream_stage_trait_t<Fun>::output>> broadcast_scatterer<typename stream_stage_trait_t<Fun>::output>>
annotated_stream<typename stream_stage_trait_t<Fun>::output, Ts...> output_stream<typename stream_stage_trait_t<Fun>::output, Ts...>
make_stage(const stream<In>& in, std::tuple<Ts...> xs, Init init, Fun fun, make_stage(const stream<In>& in, std::tuple<Ts...> xs, Init init, Fun fun,
Cleanup cleanup, policy::arg<Gatherer, Scatterer> policies = {}) Cleanup cleanup, policy::arg<Gatherer, Scatterer> policies = {})
{ CAF_IGNORE_UNUSED(policies); CAF_ASSERT(current_mailbox_element() != { CAF_IGNORE_UNUSED(policies); CAF_ASSERT(current_mailbox_element() !=
...@@ -722,16 +726,29 @@ public: ...@@ -722,16 +726,29 @@ public:
*/ */
/// @cond PRIVATE /// @cond PRIVATE
/// Builds the pipeline after receiving an `open_stream_msg`. Sends a stream
/// handshake to the next actor if `mgr->out().terminal() == false`,
/// otherwise
/// @private
sec build_pipeline(stream_manager_ptr mgr);
// -- timeout management ----------------------------------------------------- // -- timeout management -----------------------------------------------------
/// Requests a new timeout and returns its ID. /// Requests a new timeout and returns its ID.
uint32_t request_timeout(const duration& d); uint64_t set_receive_timeout(actor_clock::time_point x);
/// Requests a new timeout for the current behavior and returns its ID.
uint64_t set_receive_timeout();
/// Resets the timeout if `timeout_id` is the active timeout. /// Resets the timeout if `timeout_id` is the active timeout.
void reset_timeout(uint32_t timeout_id); void reset_receive_timeout(uint64_t timeout_id);
/// Returns whether `timeout_id` is currently active. /// Returns whether `timeout_id` is currently active.
bool is_active_timeout(uint32_t tid) const; bool is_active_receive_timeout(uint64_t tid) const;
/// Requests a new timeout and returns its ID.
uint64_t set_stream_timeout(actor_clock::time_point x);
// -- message processing ----------------------------------------------------- // -- message processing -----------------------------------------------------
...@@ -942,11 +959,15 @@ public: ...@@ -942,11 +959,15 @@ public:
void erase_inbound_paths_later(const stream_manager* mgr, void erase_inbound_paths_later(const stream_manager* mgr,
error reason) override; error reason) override;
// -- handling of upstream message -------------------------------------------
void handle(stream_slots slots, actor_addr& sender,
upstream_msg::ack_open& x);
protected: protected:
/// @cond PRIVATE /// @cond PRIVATE
/// Returns a currently unused slot. // -- utility functions for invoking default handler -------------------------
stream_slot next_slot();
/// Utility function that swaps `f` into a temporary before calling it /// Utility function that swaps `f` into a temporary before calling it
/// and restoring `f` only if it has not been replaced by the user. /// and restoring `f` only if it has not been replaced by the user.
...@@ -978,7 +999,29 @@ protected: ...@@ -978,7 +999,29 @@ protected:
swap(g, f); swap(g, f);
} }
//bool handle_stream_msg(mailbox_element& x, behavior* active_behavior); // -- timeout management -----------------------------------------------------
/// Requests a new timeout and returns its ID.
uint64_t set_timeout(atom_value type, actor_clock::time_point x);
// -- stream processing ------------------------------------------------------
/// Returns a currently unused slot.
stream_slot next_slot();
/// Adds a new stream manager to the actor and starts cycle management if
/// needed.
bool add_stream_manager(stream_slots id, stream_manager_ptr ptr);
/// Removes a new stream manager.
bool remove_stream_manager(stream_slots id);
/// @pre `x.content().match_elements<open_stream_msg>()`
invoke_message_result handle_open_stream_msg(mailbox_element& x);
/// Advances credit and batch timeouts and returns the timestamp when to call
/// this function again.
actor_clock::time_point advance_streams(actor_clock::time_point now);
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
...@@ -989,7 +1032,7 @@ protected: ...@@ -989,7 +1032,7 @@ protected:
detail::behavior_stack bhvr_stack_; detail::behavior_stack bhvr_stack_;
/// Identifies the timeout messages we are currently waiting for. /// Identifies the timeout messages we are currently waiting for.
uint32_t timeout_id_; uint64_t timeout_id_;
/// Stores callbacks for awaited responses. /// Stores callbacks for awaited responses.
std::forward_list<pending_response> awaited_responses_; std::forward_list<pending_response> awaited_responses_;
...@@ -1016,6 +1059,15 @@ protected: ...@@ -1016,6 +1059,15 @@ protected:
/// yet received an ACK. /// yet received an ACK.
std::map<stream_slot, stream_manager_ptr> pending_stream_managers_; std::map<stream_slot, stream_manager_ptr> pending_stream_managers_;
/// Controls batch and credit timeouts.
detail::tick_emitter stream_ticks_;
/// Number of ticks per batch delay.
long max_batch_delay_ticks_;
/// Number of ticks of each credit round.
long credit_round_ticks_;
/// Pointer to a private thread object associated with a detached actor. /// Pointer to a private thread object associated with a detached actor.
detail::private_thread* private_thread_; detail::private_thread* private_thread_;
......
...@@ -77,7 +77,12 @@ public: ...@@ -77,7 +77,12 @@ public:
} }
/// Returns the handler assigned to this stream on this actor. /// Returns the handler assigned to this stream on this actor.
inline const stream_manager_ptr& ptr() const { inline stream_manager_ptr& ptr() noexcept {
return ptr_;
}
/// Returns the handler assigned to this stream on this actor.
inline const stream_manager_ptr& ptr() const noexcept {
return ptr_; return ptr_;
} }
...@@ -101,7 +106,7 @@ constexpr invalid_stream_t invalid_stream = invalid_stream_t{}; ...@@ -101,7 +106,7 @@ constexpr invalid_stream_t invalid_stream = invalid_stream_t{};
/// Identifies an unbound sequence of messages annotated with the additional /// Identifies an unbound sequence of messages annotated with the additional
/// handshake arguments emitted to the next stage. /// handshake arguments emitted to the next stage.
template <class T, class... Ts> template <class T, class... Ts>
class annotated_stream final : public stream<T> { class output_stream final : public stream<T> {
public: public:
/// Dennotes the supertype. /// Dennotes the supertype.
using super = stream<T>; using super = stream<T>;
......
...@@ -36,7 +36,8 @@ namespace caf { ...@@ -36,7 +36,8 @@ namespace caf {
/// @relates stream_msg /// @relates stream_msg
class stream_manager : public ref_counted { class stream_manager : public ref_counted {
public: public:
stream_manager(local_actor* selfptr); stream_manager(local_actor* selfptr,
stream_priority prio = stream_priority::normal);
~stream_manager() override; ~stream_manager() override;
...@@ -96,12 +97,11 @@ public: ...@@ -96,12 +97,11 @@ public:
virtual void send_handshake(strong_actor_ptr dest, stream_slot slot, virtual void send_handshake(strong_actor_ptr dest, stream_slot slot,
strong_actor_ptr stream_origin, strong_actor_ptr stream_origin,
mailbox_element::forwarding_stack fwd_stack, mailbox_element::forwarding_stack fwd_stack,
message_id handshake_mid, stream_priority prio); message_id handshake_mid);
/// Sends a handshake to `dest`. /// Sends a handshake to `dest`.
/// @pre `dest != nullptr` /// @pre `dest != nullptr`
void send_handshake(strong_actor_ptr dest, stream_slot slot, void send_handshake(strong_actor_ptr dest, stream_slot slot);
stream_priority prio);
// -- implementation hooks for sources --------------------------------------- // -- implementation hooks for sources ---------------------------------------
...@@ -119,6 +119,9 @@ public: ...@@ -119,6 +119,9 @@ public:
/// safely. /// safely.
virtual bool done() const = 0; virtual bool done() const = 0;
/// Advances time.
virtual void cycle_timeout(size_t cycle_nr);
// -- input path management -------------------------------------------------- // -- input path management --------------------------------------------------
/// Informs the manager that a new input path opens. /// Informs the manager that a new input path opens.
...@@ -127,6 +130,12 @@ public: ...@@ -127,6 +130,12 @@ public:
/// Informs the manager that an input path closes. /// Informs the manager that an input path closes.
virtual void deregister_input_path(inbound_path* x) noexcept; virtual void deregister_input_path(inbound_path* x) noexcept;
// -- mutators ---------------------------------------------------------------
/// Adds a response promise to a sink for delivering the final result.
/// @pre `out().terminal() == true`
void add_promise(response_promise x);
// -- inline functions ------------------------------------------------------- // -- inline functions -------------------------------------------------------
inline local_actor* self() { inline local_actor* self() {
...@@ -170,6 +179,12 @@ protected: ...@@ -170,6 +179,12 @@ protected:
/// Keeps track of pending handshakes. /// Keeps track of pending handshakes.
long pending_handshakes_; long pending_handshakes_;
/// Configures the importance of outgoing traffic.
stream_priority priority_;
/// Stores response promises for dellivering the final result.
std::vector<response_promise> promises_;
}; };
/// A reference counting pointer to a `stream_manager`. /// A reference counting pointer to a `stream_manager`.
......
...@@ -62,12 +62,17 @@ public: ...@@ -62,12 +62,17 @@ public:
} }
/// Returns the unique identifier for this stream_result. /// Returns the unique identifier for this stream_result.
inline stream_slot slot() const { inline stream_slot slot() const noexcept {
return slot_; return slot_;
} }
/// Returns the handler assigned to this stream_result on this actor. /// Returns the handler assigned to this stream on this actor.
inline const stream_manager_ptr& ptr() const { inline stream_manager_ptr& ptr() noexcept {
return ptr_;
}
/// Returns the handler assigned to this stream on this actor.
inline const stream_manager_ptr& ptr() const noexcept {
return ptr_; return ptr_;
} }
......
...@@ -36,7 +36,7 @@ public: ...@@ -36,7 +36,7 @@ public:
using stream_type = stream<output_type>; using stream_type = stream<output_type>;
using annotated_stream_type = annotated_stream<output_type, HandshakeData...>; using output_stream_type = output_stream<output_type, HandshakeData...>;
using handshake_tuple_type = std::tuple<stream_type, HandshakeData...>; using handshake_tuple_type = std::tuple<stream_type, HandshakeData...>;
......
...@@ -39,7 +39,7 @@ public: ...@@ -39,7 +39,7 @@ public:
using stream_type = stream<output_type>; using stream_type = stream<output_type>;
using annotated_stream_type = annotated_stream<output_type, HandshakeData...>; using output_stream_type = output_stream<output_type, HandshakeData...>;
using handshake_tuple_type = std::tuple<stream_type, HandshakeData...>; using handshake_tuple_type = std::tuple<stream_type, HandshakeData...>;
......
...@@ -80,14 +80,16 @@ typename Inspector::result_type inspect(Inspector& f, group_down_msg& x) { ...@@ -80,14 +80,16 @@ typename Inspector::result_type inspect(Inspector& f, group_down_msg& x) {
/// Signalizes a timeout event. /// Signalizes a timeout event.
/// @note This message is handled implicitly by the runtime system. /// @note This message is handled implicitly by the runtime system.
struct timeout_msg { struct timeout_msg {
/// Type of the timeout (either `receive_atom` or `cycle_atom`).
atom_value type;
/// Actor-specific timeout ID. /// Actor-specific timeout ID.
uint32_t timeout_id; uint64_t timeout_id;
}; };
/// @relates timeout_msg /// @relates timeout_msg
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, timeout_msg& x) { typename Inspector::result_type inspect(Inspector& f, timeout_msg& x) {
return f(meta::type_name("timeout_msg"), x.timeout_id); return f(meta::type_name("timeout_msg"), x.type, x.timeout_id);
} }
/// Demands the receiver to open a new stream from the sender to the receiver. /// Demands the receiver to open a new stream from the sender to the receiver.
...@@ -112,7 +114,7 @@ struct open_stream_msg { ...@@ -112,7 +114,7 @@ struct open_stream_msg {
/// @relates stream_handshake_msg /// @relates stream_handshake_msg
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, open_stream_msg& x) { typename Inspector::result_type inspect(Inspector& f, open_stream_msg& x) {
return f(meta::type_name("stream_handshake_msg"), x.slot, x.msg, x.prev_stage, return f(meta::type_name("open_stream_msg"), x.slot, x.msg, x.prev_stage,
x.original_stage, x.priority); x.original_stage, x.priority);
} }
......
...@@ -73,6 +73,10 @@ public: ...@@ -73,6 +73,10 @@ public:
void operator()(const none_t&) override { void operator()(const none_t&) override {
(*this)(); (*this)();
} }
void operator()(stream_slot, stream_manager_ptr&) override {
(*this)();
}
}; };
} // namespace <anonymous> } // namespace <anonymous>
......
...@@ -259,14 +259,19 @@ bool blocking_actor::await_data(timeout_type timeout) { ...@@ -259,14 +259,19 @@ bool blocking_actor::await_data(timeout_type timeout) {
} }
mailbox_element_ptr blocking_actor::dequeue() { mailbox_element_ptr blocking_actor::dequeue() {
mailbox().fetch_more();
auto& qs = mailbox_.queue().queues(); auto& qs = mailbox_.queue().queues();
auto& q1 = get<mailbox_policy::default_queue_index>(qs); auto& q1 = get<mailbox_policy::urgent_queue_index>(qs);
auto ptr = q1.take_front(); if (!q1.empty()) {
if (ptr == nullptr) { q1.inc_deficit(1);
auto& q2 = get<mailbox_policy::urgent_queue_index>(qs); return q1.take_front();
ptr = q2.take_front();
} }
return ptr; auto& q2 = get<mailbox_policy::default_queue_index>(qs);
if (!q2.empty()) {
q2.inc_deficit(1);
return q2.take_front();
}
return nullptr;
} }
void blocking_actor::varargs_tup_receive(receive_cond& rcc, message_id mid, void blocking_actor::varargs_tup_receive(receive_cond& rcc, message_id mid,
...@@ -285,6 +290,11 @@ void blocking_actor::varargs_tup_receive(receive_cond& rcc, message_id mid, ...@@ -285,6 +290,11 @@ void blocking_actor::varargs_tup_receive(receive_cond& rcc, message_id mid,
} }
} }
sec blocking_actor::build_pipeline(stream_manager_ptr) {
CAF_LOG_ERROR("blocking_actor::connect_pipeline called");
return sec::bad_function_call;
}
size_t blocking_actor::attach_functor(const actor& x) { size_t blocking_actor::attach_functor(const actor& x) {
return attach_functor(actor_cast<strong_actor_ptr>(x)); return attach_functor(actor_cast<strong_actor_ptr>(x));
} }
......
...@@ -26,12 +26,11 @@ ...@@ -26,12 +26,11 @@
namespace caf { namespace caf {
inbound_path::stats_t::stats_t() : ring_iter(0) { inbound_path::stats_t::stats_t() : ring_iter(0) {
measurement x{0, std::chrono::nanoseconds(0)}; measurement x{0, timespan{0}};
measurements.resize(stats_sampling_size, x); measurements.resize(stats_sampling_size, x);
} }
auto inbound_path::stats_t::calculate(std::chrono::nanoseconds c, auto inbound_path::stats_t::calculate(timespan c, timespan d)
std::chrono::nanoseconds d)
-> calculation_result { -> calculation_result {
// Max throughput = C * (N / t), where C = cycle length, N = measured items, // Max throughput = C * (N / t), where C = cycle length, N = measured items,
// and t = measured time. Desired batch size is the same formula with D // and t = measured time. Desired batch size is the same formula with D
...@@ -94,8 +93,7 @@ void inbound_path::emit_ack_open(local_actor* self, actor_addr rebind_from) { ...@@ -94,8 +93,7 @@ void inbound_path::emit_ack_open(local_actor* self, actor_addr rebind_from) {
} }
void inbound_path::emit_ack_batch(local_actor* self, long queued_items, void inbound_path::emit_ack_batch(local_actor* self, long queued_items,
std::chrono::nanoseconds cycle, timespan cycle, timespan complexity) {
std::chrono::nanoseconds complexity){
auto x = stats.calculate(cycle, complexity); auto x = stats.calculate(cycle, complexity);
auto credit = std::max(x.max_throughput - (assigned_credit - queued_items), auto credit = std::max(x.max_throughput - (assigned_credit - queued_items),
0l); 0l);
......
...@@ -187,6 +187,11 @@ bool monitorable_actor::remove_backlink(abstract_actor* x) { ...@@ -187,6 +187,11 @@ bool monitorable_actor::remove_backlink(abstract_actor* x) {
return detach_impl(tk, true) > 0; return detach_impl(tk, true) > 0;
} }
error monitorable_actor::fail_state() const {
std::unique_lock<std::mutex> guard{mtx_};
return fail_state_;
}
size_t monitorable_actor::detach_impl(const attachable::token& what, size_t monitorable_actor::detach_impl(const attachable::token& what,
bool stop_on_hit, bool dry_run) { bool stop_on_hit, bool dry_run) {
CAF_LOG_TRACE(CAF_ARG(stop_on_hit) << CAF_ARG(dry_run)); CAF_LOG_TRACE(CAF_ARG(stop_on_hit) << CAF_ARG(dry_run));
......
...@@ -67,7 +67,7 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -67,7 +67,7 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
auto& tm = content.get_as<timeout_msg>(0); auto& tm = content.get_as<timeout_msg>(0);
auto tid = tm.timeout_id; auto tid = tm.timeout_id;
CAF_ASSERT(!x.mid.valid()); CAF_ASSERT(!x.mid.valid());
if (is_active_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 im_dropped;
......
This diff is collapsed.
...@@ -34,9 +34,10 @@ ...@@ -34,9 +34,10 @@
namespace caf { namespace caf {
stream_manager::stream_manager(local_actor* selfptr) stream_manager::stream_manager(local_actor* selfptr, stream_priority prio)
: self_(selfptr), : self_(selfptr),
pending_handshakes_(0) { pending_handshakes_(0),
priority_(prio) {
// nop // nop
} }
...@@ -86,6 +87,11 @@ void stream_manager::handle(stream_slots slots, upstream_msg::forced_drop& x) { ...@@ -86,6 +87,11 @@ void stream_manager::handle(stream_slots slots, upstream_msg::forced_drop& x) {
void stream_manager::close() { void stream_manager::close() {
out().close(); out().close();
self_->erase_inbound_paths_later(this); self_->erase_inbound_paths_later(this);
if (!promises_.empty()) {
auto msg = make_final_result();
for (auto& x : promises_)
x.deliver(msg);
}
} }
void stream_manager::abort(error reason) { void stream_manager::abort(error reason) {
...@@ -107,27 +113,30 @@ bool stream_manager::congested() const { ...@@ -107,27 +113,30 @@ bool stream_manager::congested() const {
void stream_manager::send_handshake(strong_actor_ptr dest, stream_slot slot, void stream_manager::send_handshake(strong_actor_ptr dest, stream_slot slot,
strong_actor_ptr client, strong_actor_ptr client,
mailbox_element::forwarding_stack fwd_stack, mailbox_element::forwarding_stack fwd_stack,
message_id mid, stream_priority prio) { message_id mid) {
CAF_ASSERT(dest != nullptr); CAF_ASSERT(dest != nullptr);
++pending_handshakes_; ++pending_handshakes_;
dest->enqueue( dest->enqueue(
make_mailbox_element( make_mailbox_element(
std::move(client), mid, std::move(fwd_stack), std::move(client), mid, std::move(fwd_stack),
open_stream_msg{slot, make_handshake(), self_->ctrl(), dest, prio}), open_stream_msg{slot, make_handshake(), self_->ctrl(), dest, priority_}),
self_->context()); self_->context());
} }
void stream_manager::send_handshake(strong_actor_ptr dest, stream_slot slot, void stream_manager::send_handshake(strong_actor_ptr dest, stream_slot slot) {
stream_priority prio) {
mailbox_element::forwarding_stack fwd_stack; mailbox_element::forwarding_stack fwd_stack;
send_handshake(std::move(dest), slot, nullptr, std::move(fwd_stack), send_handshake(std::move(dest), slot, nullptr, std::move(fwd_stack),
make_message_id(), prio); make_message_id());
} }
bool stream_manager::generate_messages() { bool stream_manager::generate_messages() {
return false; return false;
} }
void stream_manager::cycle_timeout(size_t) {
// TODO: make pure virtual
}
void stream_manager::register_input_path(inbound_path* ptr) { void stream_manager::register_input_path(inbound_path* ptr) {
inbound_paths_.emplace_back(ptr); inbound_paths_.emplace_back(ptr);
} }
...@@ -142,6 +151,12 @@ void stream_manager::deregister_input_path(inbound_path* ptr) noexcept { ...@@ -142,6 +151,12 @@ void stream_manager::deregister_input_path(inbound_path* ptr) noexcept {
inbound_paths_.pop_back(); inbound_paths_.pop_back();
} }
void stream_manager::add_promise(response_promise x) {
CAF_LOG_TRACE(CAF_ARG(x));
CAF_ASSERT(out().terminal());
promises_.emplace_back(std::move(x));
}
message stream_manager::make_final_result() { message stream_manager::make_final_result() {
return none; return none;
} }
...@@ -156,7 +171,7 @@ void stream_manager::output_closed(error) { ...@@ -156,7 +171,7 @@ void stream_manager::output_closed(error) {
} }
message stream_manager::make_handshake() const { message stream_manager::make_handshake() const {
CAF_LOG_ERROR("stream_manager::make_output_token called"); CAF_LOG_ERROR("stream_manager::make_handshake called");
return none; return none;
} }
......
...@@ -28,9 +28,6 @@ ...@@ -28,9 +28,6 @@
using namespace std; using namespace std;
using namespace caf; using namespace caf;
// TODO: use std::literals::chrono_literals when switching to C++14
using ns = std::chrono::nanoseconds;
template <class... Ts> template <class... Ts>
void print(const char* format, Ts... xs) { void print(const char* format, Ts... xs) {
char buf[200]; char buf[200];
...@@ -60,13 +57,14 @@ struct fixture { ...@@ -60,13 +57,14 @@ struct fixture {
c, n, t, m); c, n, t, m);
print("- items/batch B = max(D * N / t, 1) = max(%ld * %ld / %ld, 1) = %ld", print("- items/batch B = max(D * N / t, 1) = max(%ld * %ld / %ld, 1) = %ld",
d, n, t, b); d, n, t, b);
auto cr = x.calculate(ns(1000), ns(100)); auto cr = x.calculate(timespan(1000), timespan(100));
CAF_CHECK_EQUAL(cr.items_per_batch, b); CAF_CHECK_EQUAL(cr.items_per_batch, b);
CAF_CHECK_EQUAL(cr.max_throughput, m); CAF_CHECK_EQUAL(cr.max_throughput, m);
} }
void store(long batch_size, long calculation_time_ns) { void store(long batch_size, long calculation_time_ns) {
inbound_path::stats_t::measurement m{batch_size, ns{calculation_time_ns}}; inbound_path::stats_t::measurement m{batch_size,
timespan{calculation_time_ns}};
x.store(m); x.store(m);
} }
}; };
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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 local_streaming
#include "caf/test/dsl.hpp"
#include <memory>
#include <numeric>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/stateful_actor.hpp"
using std::string;
using std::vector;
using namespace caf;
namespace {
struct file_reader_state {
static const char* name;
};
const char* file_reader_state::name = "file_reader";
behavior file_reader(stateful_actor<file_reader_state>* self, size_t buf_size) {
using buf = std::deque<int>;
return {
[=](string& fname) -> output_stream<int, string> {
CAF_CHECK_EQUAL(fname, "numbers.txt");
CAF_CHECK_EQUAL(self->mailbox().empty(), true);
return self->make_source(
// forward file name in handshake to next stage
std::forward_as_tuple(std::move(fname)),
// initialize state
[&](buf& xs) {
xs.resize(buf_size);
std::iota(xs.begin(), xs.end(), 0);
},
// get next element
[=](buf& xs, downstream<int>& out, size_t num) {
CAF_MESSAGE("push " << num << " more messages downstream");
auto n = std::min(num, xs.size());
for (size_t i = 0; i < n; ++i)
out.push(xs[i]);
xs.erase(xs.begin(), xs.begin() + static_cast<ptrdiff_t>(n));
},
// check whether we reached the end
[=](const buf& xs) {
return xs.empty();
}
);
}
};
}
struct sum_up_state {
static const char* name;
};
const char* sum_up_state::name = "sum_up";
behavior sum_up(stateful_actor<sum_up_state>* self) {
return {
[=](stream<int>& in, string& fname) {
CAF_CHECK_EQUAL(fname, "numbers.txt");
return self->make_sink(
// input stream
in,
// initialize state
[](int& x) {
x = 0;
},
// processing step
[](int& x, int y) {
x += y;
},
// cleanup and produce result message
[](int& x) -> int {
return x;
}
);
}
};
}
struct fixture : test_coordinator_fixture<> {
};
error fail_state(const actor& x) {
auto ptr = actor_cast<abstract_actor*>(x);
return dynamic_cast<monitorable_actor&>(*ptr).fail_state();
}
} // namespace <anonymous>
// -- unit tests ---------------------------------------------------------------
CAF_TEST_FIXTURE_SCOPE(local_streaming_tests, fixture)
CAF_TEST(depth_2_pipeline) {
std::chrono::microseconds cycle{cfg.streaming_credit_round_interval_us};
sched.clock().current_time += cycle;
auto src = sys.spawn(file_reader, 10);
auto snk = sys.spawn(sum_up);
auto pipeline = snk * src;
CAF_MESSAGE(CAF_ARG(self) << CAF_ARG(src) << CAF_ARG(snk));
// self --("numbers.txt")-----------> source sink
self->send(pipeline, "numbers.txt");
expect((string), from(self).to(src).with("numbers.txt"));
// self source --(open_stream_msg)-------> sink
expect((open_stream_msg), from(self).to(snk));
// self source <--------------(ack_open)-- sink
expect((upstream_msg::ack_open), from(snk).to(src));
// self source --(batch)-----------------> sink
expect((downstream_msg::batch), from(src).to(snk));
// self source <-------------(ack_batch)-- sink
sched.clock().current_time += cycle;
sched.dispatch();
expect((timeout_msg), from(snk).to(snk));
expect((timeout_msg), from(src).to(src));
expect((upstream_msg::ack_batch), from(snk).to(src));
// self source --(close)-----------------> sink
expect((downstream_msg::close), from(src).to(snk));
// self <-----------------------------------------------------(result)-- sink
expect((int), from(snk).to(self).with(45));
CAF_CHECK_EQUAL(fail_state(snk), exit_reason::normal);
CAF_CHECK_EQUAL(fail_state(src), exit_reason::normal);
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -261,7 +261,7 @@ public: ...@@ -261,7 +261,7 @@ public:
int sentinel_; int sentinel_;
}; };
auto ptr = detail::make_stream_source<driver>(this, num_messages); auto ptr = detail::make_stream_source<driver>(this, num_messages);
ptr->send_handshake(ref.ctrl(), slot, stream_priority::normal); ptr->send_handshake(ref.ctrl(), slot);
ptr->generate_messages(); ptr->generate_messages();
pending_managers_.emplace(slot, std::move(ptr)); pending_managers_.emplace(slot, std::move(ptr));
} }
...@@ -288,7 +288,7 @@ public: ...@@ -288,7 +288,7 @@ public:
vector<int>* log_; vector<int>* log_;
}; };
forwarder = detail::make_stream_stage<driver>(this, &data); forwarder = detail::make_stream_stage<driver>(this, &data);
forwarder->send_handshake(ref.ctrl(), slot, stream_priority::normal); forwarder->send_handshake(ref.ctrl(), slot);
pending_managers_.emplace(slot, forwarder); pending_managers_.emplace(slot, forwarder);
} }
...@@ -483,8 +483,6 @@ struct msg_visitor { ...@@ -483,8 +483,6 @@ struct msg_visitor {
auto& dm = x.content().get_mutable_as<downstream_msg>(0); auto& dm = x.content().get_mutable_as<downstream_msg>(0);
auto f = detail::make_overload( auto f = detail::make_overload(
[&](downstream_msg::batch& y) { [&](downstream_msg::batch& y) {
TRACE(self->name(), batch, CAF_ARG2("size", y.xs_size),
CAF_ARG2("remaining_credit", inptr->assigned_credit - y.xs_size));
inptr->handle(y); inptr->handle(y);
if (inptr->mgr->done()) { if (inptr->mgr->done()) {
CAF_MESSAGE(self->name() CAF_MESSAGE(self->name()
...@@ -599,7 +597,7 @@ struct fixture { ...@@ -599,7 +597,7 @@ struct fixture {
template <class... Ts> template <class... Ts>
void next_cycle(Ts&... xs) { void next_cycle(Ts&... xs) {
entity* es[] = {&xs...}; entity* es[] = {&xs...};
CAF_MESSAGE("advance clock by " << tc.credit_interval.count() << "ns"); //CAF_MESSAGE("advance clock by " << tc.credit_interval.count() << "ns");
tc.global_time += tc.credit_interval; tc.global_time += tc.credit_interval;
for (auto e : es) for (auto e : es)
e->advance_time(); e->advance_time();
...@@ -650,7 +648,7 @@ CAF_TEST(depth_2_pipeline_single_round) { ...@@ -650,7 +648,7 @@ CAF_TEST(depth_2_pipeline_single_round) {
} }
CAF_TEST(depth_2_pipeline_multiple_rounds) { CAF_TEST(depth_2_pipeline_multiple_rounds) {
constexpr size_t num_messages = 200000; constexpr size_t num_messages = 2000;
alice.start_streaming(bob, num_messages); alice.start_streaming(bob, num_messages);
loop_until([&] { return done_streaming(); }, alice, bob); loop_until([&] { return done_streaming(); }, alice, bob);
CAF_CHECK_EQUAL(bob.data, make_iota(0, num_messages)); CAF_CHECK_EQUAL(bob.data, make_iota(0, num_messages));
...@@ -667,7 +665,7 @@ CAF_TEST(depth_3_pipeline_single_round) { ...@@ -667,7 +665,7 @@ CAF_TEST(depth_3_pipeline_single_round) {
} }
CAF_TEST(depth_3_pipeline_multiple_rounds) { CAF_TEST(depth_3_pipeline_multiple_rounds) {
constexpr size_t num_messages = 200000; constexpr size_t num_messages = 2000;
bob.forward_to(carl); bob.forward_to(carl);
alice.start_streaming(bob, num_messages); alice.start_streaming(bob, num_messages);
CAF_MESSAGE("loop over alice and bob until bob is congested"); CAF_MESSAGE("loop over alice and bob until bob is congested");
......
...@@ -322,8 +322,13 @@ struct fixture { ...@@ -322,8 +322,13 @@ struct fixture {
actor_system system; actor_system system;
scoped_actor self; scoped_actor self;
fixture() : system(cfg.add_message_type<get_state_msg>("get_state_msg")), static actor_system_config& init(actor_system_config& cfg) {
self(system) { cfg.add_message_type<get_state_msg>("get_state_msg");
cfg.parse(test::engine::argc(), test::engine::argv());
return cfg;
}
fixture() : system(init(cfg)), self(system) {
// nop // nop
} }
...@@ -376,10 +381,11 @@ CAF_TEST_FIXTURE_SCOPE(typed_spawn_tests, fixture) ...@@ -376,10 +381,11 @@ CAF_TEST_FIXTURE_SCOPE(typed_spawn_tests, fixture)
******************************************************************************/ ******************************************************************************/
CAF_TEST(typed_spawns) { CAF_TEST(typed_spawns) {
// run test series with typed_server(1|2) CAF_MESSAGE("run test series with typed_server1");
test_typed_spawn(system.spawn(typed_server1)); test_typed_spawn(system.spawn(typed_server1));
self->await_all_other_actors_done(); self->await_all_other_actors_done();
CAF_MESSAGE("finished test series with `typed_server1`"); CAF_MESSAGE("finished test series with `typed_server1`");
CAF_MESSAGE("run test series with typed_server2");
test_typed_spawn(system.spawn(typed_server2)); test_typed_spawn(system.spawn(typed_server2));
self->await_all_other_actors_done(); self->await_all_other_actors_done();
CAF_MESSAGE("finished test series with `typed_server2`"); CAF_MESSAGE("finished test series with `typed_server2`");
......
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