Commit df9971c1 authored by Dominik Charousset's avatar Dominik Charousset

Re-organize filtering and bugfixing

parent 7787c1b8
...@@ -80,6 +80,8 @@ public: ...@@ -80,6 +80,8 @@ public:
actor& operator=(actor&&) = default; actor& operator=(actor&&) = default;
actor& operator=(const actor&) = default; actor& operator=(const actor&) = default;
actor(std::nullptr_t);
actor(const scoped_actor&); actor(const scoped_actor&);
explicit actor(const unsafe_actor_handle_init_t&) CAF_DEPRECATED; explicit actor(const unsafe_actor_handle_init_t&) CAF_DEPRECATED;
...@@ -108,12 +110,9 @@ public: ...@@ -108,12 +110,9 @@ public:
return *this; return *this;
} }
actor& operator=(const scoped_actor& x); actor& operator=(std::nullptr_t);
inline actor& operator=(std::nullptr_t) { actor& operator=(const scoped_actor& x);
ptr_.reset();
return *this;
}
/// Queries whether this actor handle is valid. /// Queries whether this actor handle is valid.
inline explicit operator bool() const { inline explicit operator bool() const {
......
...@@ -106,7 +106,7 @@ public: ...@@ -106,7 +106,7 @@ public:
actor_system_config(); actor_system_config();
actor_system_config(actor_system_config&&) = default; actor_system_config(actor_system_config&&);
actor_system_config(const actor_system_config&) = delete; actor_system_config(const actor_system_config&) = delete;
actor_system_config& operator=(const actor_system_config&) = delete; actor_system_config& operator=(const actor_system_config&) = delete;
...@@ -276,8 +276,8 @@ public: ...@@ -276,8 +276,8 @@ public:
// -- backward compatibility ------------------------------------------------- // -- backward compatibility -------------------------------------------------
std::string& logger_filename CAF_DEPRECATED = logger_file_name; std::string& logger_filename CAF_DEPRECATED;
std::string& logger_filter CAF_DEPRECATED = logger_component_filter; std::string& logger_filter CAF_DEPRECATED;
// -- config parameters of the middleman ------------------------------------- // -- config parameters of the middleman -------------------------------------
......
...@@ -77,6 +77,8 @@ ...@@ -77,6 +77,8 @@
#include "caf/message_builder.hpp" #include "caf/message_builder.hpp"
#include "caf/message_handler.hpp" #include "caf/message_handler.hpp"
#include "caf/response_handle.hpp" #include "caf/response_handle.hpp"
#include "caf/fused_scatterer.hpp"
#include "caf/random_gatherer.hpp"
#include "caf/system_messages.hpp" #include "caf/system_messages.hpp"
#include "caf/abstract_channel.hpp" #include "caf/abstract_channel.hpp"
#include "caf/may_have_timeout.hpp" #include "caf/may_have_timeout.hpp"
...@@ -87,12 +89,15 @@ ...@@ -87,12 +89,15 @@
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/primitive_variant.hpp" #include "caf/primitive_variant.hpp"
#include "caf/timeout_definition.hpp" #include "caf/timeout_definition.hpp"
#include "caf/broadcast_scatterer.hpp"
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/composable_behavior.hpp" #include "caf/composable_behavior.hpp"
#include "caf/typed_actor_pointer.hpp" #include "caf/typed_actor_pointer.hpp"
#include "caf/scoped_execution_unit.hpp" #include "caf/scoped_execution_unit.hpp"
#include "caf/typed_response_promise.hpp" #include "caf/typed_response_promise.hpp"
#include "caf/random_topic_scatterer.hpp"
#include "caf/broadcast_topic_scatterer.hpp"
#include "caf/typed_event_based_actor.hpp" #include "caf/typed_event_based_actor.hpp"
#include "caf/abstract_composable_behavior.hpp" #include "caf/abstract_composable_behavior.hpp"
......
...@@ -40,6 +40,7 @@ public: ...@@ -40,6 +40,7 @@ public:
} }
void emit_batches() override { void emit_batches() override {
CAF_LOG_TRACE("");
auto chunk = this->get_chunk(this->min_credit()); auto chunk = this->get_chunk(this->min_credit());
auto csize = static_cast<long>(chunk.size()); auto csize = static_cast<long>(chunk.size());
CAF_LOG_TRACE(CAF_ARG(chunk)); CAF_LOG_TRACE(CAF_ARG(chunk));
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_TOPIC_SCATTERER_HPP #ifndef CAF_BROADCAST_TOPIC_SCATTERER_HPP
#define CAF_TOPIC_SCATTERER_HPP #define CAF_BROADCAST_TOPIC_SCATTERER_HPP
#include <map> #include <map>
#include <tuple> #include <tuple>
...@@ -26,19 +26,17 @@ ...@@ -26,19 +26,17 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include "caf/buffered_scatterer.hpp" #include "caf/topic_scatterer.hpp"
namespace caf { namespace caf {
/// A topic scatterer that delivers data in broadcast fashion to all sinks. /// A topic scatterer that delivers data in broadcast fashion to all sinks.
template <class T, class Filter, template <class T, class Filter, class Select>
class KeyCompare = std::equal_to<typename Filter::value_type>,
long KeyIndex = 0>
class broadcast_topic_scatterer class broadcast_topic_scatterer
: public topic_scatterer<T, Filter, KeyCompare, KeyIndex> { : public topic_scatterer<T, Filter, Select> {
public: public:
/// Base type. /// Base type.
using super = buffered_scatterer<T>; using super = topic_scatterer<T, Filter, Select>;
broadcast_topic_scatterer(local_actor* selfptr) : super(selfptr) { broadcast_topic_scatterer(local_actor* selfptr) : super(selfptr) {
// nop // nop
...@@ -51,6 +49,7 @@ public: ...@@ -51,6 +49,7 @@ public:
} }
void emit_batches() override { void emit_batches() override {
CAF_LOG_TRACE("");
this->fan_out(); this->fan_out();
for (auto& kvp : this->lanes_) { for (auto& kvp : this->lanes_) {
auto& l = kvp.second; auto& l = kvp.second;
...@@ -60,8 +59,8 @@ public: ...@@ -60,8 +59,8 @@ public:
continue; continue;
auto wrapped_chunk = make_message(std::move(chunk)); auto wrapped_chunk = make_message(std::move(chunk));
for (auto& x : l.paths) { for (auto& x : l.paths) {
x->open_credit -= csize; CAF_ASSERT(x->open_credit >= csize);
this->emit_batch(*x, static_cast<size_t>(csize), wrapped_chunk); x->emit_batch(csize, wrapped_chunk);
} }
} }
} }
...@@ -69,4 +68,4 @@ public: ...@@ -69,4 +68,4 @@ public:
} // namespace caf } // namespace caf
#endif // CAF_TOPIC_SCATTERER_HPP #endif // CAF_BROADCAST_TOPIC_SCATTERER_HPP
...@@ -17,57 +17,66 @@ ...@@ -17,57 +17,66 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_STREAM_SINK_HPP #ifndef CAF_DETAIL_APPLY_ARGS_HPP
#define CAF_STREAM_SINK_HPP #define CAF_DETAIL_APPLY_ARGS_HPP
#include <vector> #include <utility>
#include "caf/extend.hpp" #include "caf/detail/int_list.hpp"
#include "caf/message_id.hpp" #include "caf/detail/type_list.hpp"
#include "caf/stream_manager.hpp"
#include "caf/upstream_policy.hpp"
#include "caf/actor_control_block.hpp"
namespace caf { namespace caf {
namespace detail {
/// Represents a terminal stream stage.
class stream_sink : public stream_manager { // this utterly useless function works around a bug in Clang that causes
public: // the compiler to reject the trailing return type of apply_args because
stream_sink(upstream_policy* in_ptr, strong_actor_ptr&& orig_sender, // "get" is not defined (it's found during ADL)
std::vector<strong_actor_ptr>&& trailing_stages, message_id mid); template<long Pos, class... Ts>
typename tl_at<type_list<Ts...>, Pos>::type get(const type_list<Ts...>&);
bool done() const override;
template <class F, long... Is, class Tuple>
error batch(const actor_addr& hdl, long xs_size, message& xs, auto apply_args(F& f, detail::int_list<Is...>, Tuple& tup)
int64_t xs_id) override; -> decltype(f(get<Is>(tup)...)) {
return f(get<Is>(tup)...);
void abort(error reason) override; }
void last_upstream_closed(); template <class F, long... Is, class Tuple>
auto apply_moved_args(F& f, detail::int_list<Is...>, Tuple& tup)
inline upstream_policy& in() { -> decltype(f(std::move(get<Is>(tup))...)) {
return *in_ptr_; return f(std::move(get<Is>(tup))...);
} }
long min_buffer_size() const { template <class F, class Tuple, class... Ts>
return min_buffer_size_; auto apply_args_prefixed(F& f, detail::int_list<>, Tuple&, Ts&&... xs)
} -> decltype(f(std::forward<Ts>(xs)...)) {
return f(std::forward<Ts>(xs)...);
protected: }
/// Consumes a batch.
virtual error consume(message& xs) = 0; template <class F, long... Is, class Tuple, class... Ts>
auto apply_args_prefixed(F& f, detail::int_list<Is...>, Tuple& tup, Ts&&... xs)
/// Computes the final result after consuming all batches of the stream. -> decltype(f(std::forward<Ts>(xs)..., get<Is>(tup)...)) {
virtual message finalize() = 0; return f(std::forward<Ts>(xs)..., get<Is>(tup)...);
}
private:
upstream_policy* in_ptr_; template <class F, class Tuple, class... Ts>
strong_actor_ptr original_sender_; auto apply_moved_args_prefixed(F& f, detail::int_list<>, Tuple&, Ts&&... xs)
std::vector<strong_actor_ptr> next_stages_; -> decltype(f(std::forward<Ts>(xs)...)) {
message_id original_msg_id_; return f(std::forward<Ts>(xs)...);
long min_buffer_size_; }
};
template <class F, long... Is, class Tuple, class... Ts>
auto apply_moved_args_prefixed(F& f, detail::int_list<Is...>, Tuple& tup, Ts&&... xs)
-> decltype(f(std::forward<Ts>(xs)..., std::move(get<Is>(tup))...)) {
return f(std::forward<Ts>(xs)..., std::move(get<Is>(tup))...);
}
template <class F, long... Is, class Tuple, class... Ts>
auto apply_args_suffxied(F& f, detail::int_list<Is...>, Tuple& tup, Ts&&... xs)
-> decltype(f(get<Is>(tup)..., std::forward<Ts>(xs)...)) {
return f(get<Is>(tup)..., std::forward<Ts>(xs)...);
}
} // namespace detail
} // namespace caf } // namespace caf
#endif // CAF_STREAM_SINK_HPP #endif // CAF_DETAIL_APPLY_ARGS_HPP
...@@ -122,6 +122,11 @@ public: ...@@ -122,6 +122,11 @@ public:
(*this)(); (*this)();
} }
template <class T>
void operator()(stream<T>&) {
(*this)();
}
// visit API - returns true if T was visited, false if T was skipped // visit API - returns true if T was visited, false if T was skipped
template <class T> template <class T>
...@@ -138,6 +143,11 @@ public: ...@@ -138,6 +143,11 @@ public:
return false; return false;
} }
template <class T>
bool visit(stream<T>&) {
return true;
}
inline bool visit(optional<skip_t>& x) { inline bool visit(optional<skip_t>& x) {
if (x) if (x)
return false; return false;
......
...@@ -35,6 +35,10 @@ public: ...@@ -35,6 +35,10 @@ public:
// nop // nop
} }
using super::min_batch_size;
using super::max_batch_size;
using super::min_buffer_size;
long min_batch_size() const override { long min_batch_size() const override {
return 1; return 1;
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2017 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_DETAIL_STREAM_DISTRIBUTION_TREE_HPP
#define CAF_DETAIL_STREAM_DISTRIBUTION_TREE_HPP
#include <memory>
#include <unordered_map>
#include "caf/ref_counted.hpp"
#include "caf/stream_manager.hpp"
#include "caf/random_gatherer.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/broadcast_scatterer.hpp"
namespace caf {
namespace detail {
/// A stream distribution tree consist of peers forming an acyclic graph. The
/// user is responsible for making sure peers do not form a loop. Data is
/// flooded along the tree. Each peer serves any number of subscribers. The
/// policy of the tree enables subscriptions to different chunks of the whole
/// stream (substreams).
///
/// The tree uses two CAF streams between each pair of peers for transmitting
/// data. This automatically adds backpressure to the system, i.e., no peer can
/// overwhelm others.
///
/// Policies need to provide the following member types and functions:
///
/// ~~~{.cpp}
/// struct policy {
/// /// Any number of user-defined arguments.
/// policy(...);
///
/// /// Tuple of available substream scatterers to subscribers. Each
/// /// element of the tuple is a subtype of `topic_scatterer<T>` (or
/// /// provides a similar interface).
/// using substream_scatterers = ...;
///
/// /// A compile-time index type identifying individual substreams.
/// using substream_index_type = ...;
///
/// /// Represent a single topic for filtering stream data.
/// using topic_type = ...;
///
/// /// Groups multiple topics into a single selection filter.
/// using filter_type = ...;
///
/// /// Policy for gathering data from peers.
/// using gatherer_type = ...;
///
/// /// Policy for scattering data to peers.
/// using scatterer_type = ...;
///
/// /// Decides whether a filter applies to a given message.
/// bool selected(const filter_type& sieve, const message& sand) const;
///
/// // + one overload to `selected` for each substream data type.
///
/// /// Accesses individual substream scatterers by index.
/// auto& substream(substream_scatterers&, substream_index_type);
///
/// /// Accesses individual substream scatterers by stream ID.
/// stream_scatterer* substream(substream_scatterers&, const stream_id&);
///
/// /// Returns true if the substreams have no data pending and the policy
/// /// decides to shutdown this peer.
/// bool at_end() const;
///
/// /// Returns true if outgoing data originating on this peer is
/// /// forwarded to all substreams. Otherwise, the substreams receive data
/// /// produced on other peers exclusively.
/// bool substream_local_data() const;
///
/// /// Handles a batch if matches the data types managed by any of the
/// /// substreams. Returns `none` if the batch is a peer message, otherwise
/// /// the error resulting from handling the batch.
/// optional<error> batch(const stream_id& sid, const actor_addr& hdl,
/// long xs_size, message& xs, int64_t xs_id);
///
/// ///
/// void push_to_substreams(message msg);
/// };
/// ~~~
template <class Policy>
class stream_distribution_tree : public stream_manager {
public:
// -- nested types -----------------------------------------------------------
using super = stream_manager;
using gatherer_type = typename Policy::gatherer_type;
using scatterer_type = typename Policy::scatterer_type;
// --- constructors and destructors ------------------------------------------
template <class... Ts>
stream_distribution_tree(scheduled_actor* selfptr, Ts&&... xs)
: self_(selfptr),
in_(selfptr),
out_(selfptr),
policy_(this, std::forward<Ts>(xs)...) {
// nop
}
~stream_distribution_tree() override {
// nop
}
// -- Accessors --------------------------------------------------------------
inline Policy& policy() {
return policy_;
}
inline const Policy& policy() const {
return policy_;
}
void close_remote_input();
// -- Overridden member functions of `stream_manager` ------------------------
/// Terminates the core actor with log output `log_message` if `at_end`
/// returns `true`.
void shutdown_if_at_end(const char* log_message) {
CAF_IGNORE_UNUSED(log_message);
if (policy_.at_end()) {
CAF_LOG_DEBUG(log_message);
self_->quit(caf::exit_reason::user_shutdown);
}
}
// -- overridden member functions of `stream_manager` ------------------------
error ack_open(const stream_id& sid, const actor_addr& rebind_from,
strong_actor_ptr rebind_to, long initial_demand,
bool redeployable) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(rebind_from) << CAF_ARG(rebind_to)
<< CAF_ARG(initial_demand) << CAF_ARG(redeployable));
auto res = super::ack_open(sid, rebind_from, rebind_to, initial_demand,
redeployable);
if (res == none)
policy_.ack_open_success(sid, rebind_from, rebind_to);
else
policy_.ack_open_failure(sid, rebind_from, rebind_to, res);
return res;
}
error process_batch(message& xs) override {
CAF_LOG_TRACE(CAF_ARG(xs));
policy_.handle_batch(xs);
return none;
}
error batch(const stream_id& sid, const actor_addr& hdl, long xs_size,
message& xs, int64_t xs_id) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(hdl) << CAF_ARG(xs_size)
<< CAF_ARG(xs) << CAF_ARG(xs_id));
policy_.before_handle_batch(sid, hdl, xs_size, xs, xs_id);
auto res = super::batch(sid, hdl, xs_size, xs, xs_id);
policy_.after_handle_batch(sid, hdl, xs_id);
push();
return res;
}
bool done() const override {
return false;
}
gatherer_type& in() override {
return in_;
}
scatterer_type& out() override {
return out_;
}
void downstream_demand(outbound_path*, long) override {
CAF_LOG_TRACE("");
push();
in_.assign_credit(out_.credit());
}
error close(const stream_id& sid, const actor_addr& hdl) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(hdl));
if (in_.remove_path(sid, hdl, none, true))
return policy_.path_closed(sid, hdl);
return none;
}
error drop(const stream_id& sid, const actor_addr& hdl) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(hdl));
if (out_.remove_path(sid, hdl, none, true))
return policy_.path_dropped(sid, hdl);
return none;
}
error forced_close(const stream_id& sid, const actor_addr& hdl,
error reason) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(hdl) << CAF_ARG(reason));
if (in_.remove_path(sid, hdl, reason, true))
return policy_.path_force_closed(sid, hdl, std::move(reason));
return none;
}
error forced_drop(const stream_id& sid, const actor_addr& hdl,
error reason) override {
if (out_.remove_path(sid, hdl, reason, true))
return policy_.path_force_dropped(sid, hdl, std::move(reason));
return none;
}
scheduled_actor* self() {
return self_;
}
private:
scheduled_actor* self_;
gatherer_type in_;
scatterer_type out_;
Policy policy_;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_STREAM_DISTRIBUTION_TREE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2017 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_FUSED_SCATTERER
#define CAF_FUSED_SCATTERER
#include <tuple>
#include <cstddef>
#include "caf/logger.hpp"
#include "caf/stream_scatterer.hpp"
namespace caf {
namespace detail {
/// Utility function for repeating `x` for a given template parameter pack.
template <class T, class U>
U pack_repeat(U x) {
return x;
}
template <class Iter>
class ptr_array_initializer {
public:
ptr_array_initializer(Iter first) : i_(first) {
// nop
}
void operator()() {
// end of recursion
}
template <class T, class... Ts>
void operator()(T& x, Ts&... xs) {
*i_ = &x;
++i_;
(*this)(xs...);
}
private:
Iter i_;
};
struct scatterer_selector {
inline stream_scatterer* operator()(const message&) {
return nullptr;
}
template <class T, class... Ts>
stream_scatterer* operator()(const message& msg, T& x, Ts&... xs) {
if (msg.match_element<stream<typename T::value_type>>(0))
return &x;
return (*this)(msg, xs...);
}
};
} // namespace detail
/// A scatterer that delegates to any number of sub-scatterers. Data is only
/// pushed to the main scatterer `T` per default.
template <class T, class... Ts>
class fused_scatterer : public stream_scatterer {
public:
using substreams_tuple = std::tuple<T, Ts...>;
using pointer = stream_scatterer*;
using const_pointer = const pointer;
using iterator = pointer*;
using const_iterator = const pointer*;
fused_scatterer(local_actor* self)
: substreams_(self, detail::pack_repeat<Ts>(self)...) {
detail::ptr_array_initializer<pointer*> f{ptrs_};
auto indices = detail::get_indices(substreams_);
detail::apply_args(f, indices, substreams_);
}
path_ptr add_path(const stream_id& sid, strong_actor_ptr origin,
strong_actor_ptr sink_ptr,
mailbox_element::forwarding_stack stages,
message_id handshake_mid, message handshake_data,
stream_priority prio, bool redeployable) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(origin) << CAF_ARG(sink_ptr)
<< CAF_ARG(stages) << CAF_ARG(handshake_mid)
<< CAF_ARG(handshake_data) << CAF_ARG(prio)
<< CAF_ARG(redeployable));
auto ptr = substream_by_handshake_type(handshake_data);
if (!ptr)
return nullptr;
return ptr->add_path(sid, std::move(origin), std::move(sink_ptr),
std::move(stages), handshake_mid,
std::move(handshake_data), prio, redeployable);
}
path_ptr confirm_path(const stream_id& sid, const actor_addr& from,
strong_actor_ptr to, long initial_demand,
bool redeployable) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(from) << CAF_ARG(to)
<< CAF_ARG(initial_demand) << CAF_ARG(redeployable));
return first_hit([&](pointer ptr) -> outbound_path* {
// Note: we cannot blindly try `confirm_path` on each scatterer, because
// this will trigger forced_close messages.
if (ptr->find(sid, from) == nullptr)
return nullptr;
return ptr->confirm_path(sid, from, to, initial_demand, redeployable);
});
}
bool remove_path(const stream_id& sid, const actor_addr& addr, error reason,
bool silent) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(addr) << CAF_ARG(reason)
<< CAF_ARG(silent));
return std::any_of(begin(), end(), [&](pointer x) {
return x->remove_path(sid, addr, reason, silent);
});
}
bool paths_clean() const override {
return std::all_of(begin(), end(),
[&](const_pointer x) { return x->paths_clean(); });
}
void close() override {
CAF_LOG_TRACE("");
for (auto ptr : ptrs_)
ptr->close();
}
void abort(error reason) override {
CAF_LOG_TRACE(CAF_ARG(reason));
for (auto ptr : ptrs_)
ptr->abort(reason);
}
long num_paths() const override {
return std::accumulate(begin(), end(), 0l, [](long x, const_pointer y) {
return x + y->num_paths();
});
}
bool closed() const override {
return std::all_of(begin(), end(),
[&](const_pointer x) { return x->closed(); });
}
bool continuous() const override {
return std::any_of(begin(), end(),
[&](const_pointer x) { return x->continuous(); });
}
void continuous(bool value) override {
for (auto ptr : ptrs_)
ptr->continuous(value);
}
void emit_batches() override {
CAF_LOG_TRACE("");
for (auto ptr : ptrs_)
ptr->emit_batches();
}
path_ptr find(const stream_id& sid, const actor_addr& x) override{
return first_hit([&](const_pointer ptr) { return ptr->find(sid, x); });
}
path_ptr path_at(size_t idx) override {
auto i = std::begin(ptrs_);
auto e = std::end(ptrs_);
while (i != e) {
auto np = static_cast<size_t>((*i)->num_paths());
if (idx < np)
return (*i)->path_at(idx);
idx -= np;
}
return nullptr;
}
long credit() const override {
return std::accumulate(
begin(), end(), std::numeric_limits<long>::max(),
[](long x, pointer y) { return std::min(x, y->credit()); });
}
long buffered() const override {
return std::accumulate(begin(), end(), 0l, [](long x, const_pointer y) {
return x + y->buffered();
});
}
long min_batch_size() const override {
return main_stream().min_batch_size();
}
long max_batch_size() const override {
return main_stream().max_batch_size();
}
long min_buffer_size() const override {
return main_stream().min_buffer_size();
}
duration max_batch_delay() const override {
return main_stream().max_batch_delay();
}
void min_batch_size(long x) override {
main_stream().min_batch_size(x);
}
void max_batch_size(long x) override {
main_stream().max_batch_size(x);
}
void min_buffer_size(long x) override {
main_stream().min_buffer_size(x);
}
void max_batch_delay(duration x) override {
main_stream().max_batch_delay(x);
}
template <class... Us>
void push(Us&&... xs) {
main_stream().push(std::forward<Us>(xs)...);
}
iterator begin() {
return std::begin(ptrs_);
}
const_iterator begin() const {
return std::begin(ptrs_);
}
iterator end() {
return std::end(ptrs_);
}
const_iterator end() const {
return std::end(ptrs_);
}
T& main_stream() {
return std::get<0>(substreams_);
}
const T& main_stream() const {
return std::get<0>(substreams_);
}
template <size_t I>
typename std::tuple_element<I, substreams_tuple>::type& substream() {
return std::get<I>(substreams_);
}
template <size_t I>
const typename std::tuple_element<I, substreams_tuple>::type&
substream() const {
return std::get<I>(substreams_);
}
stream_scatterer* substream_by_handshake_type(const message& msg) {
detail::scatterer_selector f;
auto indices = detail::get_indices(substreams_);
return detail::apply_args_prefixed(f, indices, substreams_, msg);
}
private:
template <class F>
path_ptr first_hit(F f) {
for (auto ptr : ptrs_) {
auto result = f(ptr);
if (result != nullptr)
return result;
}
return nullptr;
}
substreams_tuple substreams_;
stream_scatterer* ptrs_[sizeof...(Ts) + 1];
};
} // namespace caf
#endif // CAF_FUSED_SCATTERER
...@@ -43,6 +43,9 @@ template <class> struct timeout_definition; ...@@ -43,6 +43,9 @@ template <class> struct timeout_definition;
template <class, class, int> class actor_cast_access; template <class, class, int> class actor_cast_access;
template <class, class, class> class broadcast_topic_scatterer;
template <class, class, class> class random_topic_scatterer;
// -- variadic templates ------------------------------------------------------- // -- variadic templates -------------------------------------------------------
template <class...> class result; template <class...> class result;
...@@ -54,6 +57,7 @@ template <class...> class typed_event_based_actor; ...@@ -54,6 +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 fused_scatterer;
template <class, class...> class annotated_stream; template <class, class...> class annotated_stream;
// -- classes ------------------------------------------------------------------ // -- classes ------------------------------------------------------------------
...@@ -74,7 +78,6 @@ class serializer; ...@@ -74,7 +78,6 @@ class serializer;
class actor_proxy; class actor_proxy;
class local_actor; class local_actor;
class ref_counted; class ref_counted;
class stream_sink;
class actor_config; class actor_config;
class actor_system; class actor_system;
class deserializer; class deserializer;
...@@ -83,7 +86,6 @@ class message_view; ...@@ -83,7 +86,6 @@ class message_view;
class scoped_actor; class scoped_actor;
class inbound_path; class inbound_path;
class outbound_path; class outbound_path;
class stream_source;
class abstract_actor; class abstract_actor;
class abstract_group; class abstract_group;
class actor_registry; class actor_registry;
...@@ -91,6 +93,7 @@ class blocking_actor; ...@@ -91,6 +93,7 @@ class blocking_actor;
class execution_unit; class execution_unit;
class proxy_registry; class proxy_registry;
class stream_manager; class stream_manager;
class random_gatherer;
class stream_gatherer; class stream_gatherer;
class actor_companion; class actor_companion;
class mailbox_element; class mailbox_element;
...@@ -184,6 +187,7 @@ class manager; ...@@ -184,6 +187,7 @@ class manager;
namespace detail { namespace detail {
template <class> class type_erased_value_impl; template <class> class type_erased_value_impl;
template <class> class stream_distribution_tree;
class disposer; class disposer;
class message_data; class message_data;
......
...@@ -31,13 +31,11 @@ ...@@ -31,13 +31,11 @@
namespace caf { namespace caf {
/// A topic scatterer that delivers data to sinks in random order. /// A topic scatterer that delivers data to sinks in random order.
template <class T, class Filter, template <class T, class Filter, class Select>
class KeyCompare = std::equal_to<typename Filter::value_type>,
long KeyIndex = 0>
class random_topic_scatterer class random_topic_scatterer
: public topic_scatterer<T, Filter, KeyCompare, KeyIndex> { : public topic_scatterer<T, Filter, Select> {
public: public:
using super = topic_scatterer<T, Filter, KeyCompare, KeyIndex>; using super = topic_scatterer<T, Filter, Select>;
random_topic_scatterer(local_actor* selfptr) : super(selfptr) { random_topic_scatterer(local_actor* selfptr) : super(selfptr) {
// nop // nop
...@@ -50,6 +48,7 @@ public: ...@@ -50,6 +48,7 @@ public:
} }
void emit_batches() override { void emit_batches() override {
CAF_LOG_TRACE("");
this->fan_out(); this->fan_out();
for (auto& kvp : this->lanes_) { for (auto& kvp : this->lanes_) {
auto& l = kvp.second; auto& l = kvp.second;
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "caf/stream_source_impl.hpp" #include "caf/stream_source_impl.hpp"
#include "caf/stream_result_trait.hpp" #include "caf/stream_result_trait.hpp"
#include "caf/broadcast_scatterer.hpp" #include "caf/broadcast_scatterer.hpp"
#include "caf/terminal_stream_scatterer.hpp"
#include "caf/to_string.hpp" #include "caf/to_string.hpp"
...@@ -481,8 +482,6 @@ public: ...@@ -481,8 +482,6 @@ public:
CAF_IGNORE_UNUSED(policies); CAF_IGNORE_UNUSED(policies);
CAF_ASSERT(current_mailbox_element() != nullptr); CAF_ASSERT(current_mailbox_element() != nullptr);
CAF_ASSERT(current_mailbox_element()->content().match_elements<stream_msg>()); CAF_ASSERT(current_mailbox_element()->content().match_elements<stream_msg>());
auto& sm = current_mailbox_element()->content().get_as<stream_msg>(0);
CAF_ASSERT(holds_alternative<stream_msg::open>(sm.content));
using output_type = typename stream_stage_trait_t<Fun>::output; using output_type = typename stream_stage_trait_t<Fun>::output;
using state_type = typename stream_stage_trait_t<Fun>::state; using state_type = typename stream_stage_trait_t<Fun>::state;
static_assert(std::is_same< static_assert(std::is_same<
...@@ -526,6 +525,36 @@ public: ...@@ -526,6 +525,36 @@ public:
std::move(cleanup), policies); std::move(cleanup), policies);
} }
/// Creates a new stream sink of type T.
/// @pre `current_mailbox_element()` is a `stream_msg::open` handshake
/// @param in The input of the sink.
/// @param f Callback for initializing the object after successful creation.
/// @param xs Parameter pack for creating the instance of T.
/// @returns A stream object with a pointer to the generated `stream_manager`.
template <class T, class In, class SuccessCallback, class... Ts>
stream_result<typename T::output_type>
make_sink_impl(const stream<In>& in, SuccessCallback f, Ts&&... xs) {
CAF_ASSERT(current_mailbox_element() != nullptr);
CAF_ASSERT(current_mailbox_element()->content().match_elements<stream_msg>());
auto& sm = current_mailbox_element()->content().get_as<stream_msg>(0);
CAF_ASSERT(holds_alternative<stream_msg::open>(sm.content));
auto& opn = get<stream_msg::open>(sm.content);
auto sid = in.id();
auto next = take_current_next_stage();
auto ptr = make_counted<T>(this, std::forward<Ts>(xs)...);
auto rp = make_response_promise();
if (!add_source(ptr, sid, std::move(opn.prev_stage),
std::move(opn.original_stage), opn.priority,
opn.redeployable, rp)) {
CAF_LOG_ERROR("cannot create stream stage without source");
rp.deliver(sec::cannot_add_upstream);
return none;
}
f(*ptr);
streams_.emplace(in.id(), ptr);
return {in.id(), std::move(ptr)};
}
/// Creates a new stream sink. /// Creates a new stream sink.
/// @pre `current_mailbox_element()` is a `stream_msg::open` handshake /// @pre `current_mailbox_element()` is a `stream_msg::open` handshake
/// @param in The input of the sink. /// @param in The input of the sink.
...@@ -535,16 +564,12 @@ public: ...@@ -535,16 +564,12 @@ public:
/// @param gatherer_type Sets the policy for upstream communication. /// @param gatherer_type Sets the policy for upstream communication.
/// @returns A stream object with a pointer to the generated `stream_manager`. /// @returns A stream object with a pointer to the generated `stream_manager`.
template <class In, class Init, class Fun, class Finalize, template <class In, class Init, class Fun, class Finalize,
class Gatherer = random_gatherer> class Gatherer = random_gatherer,
class Scatterer = terminal_stream_scatterer>
stream_result<typename stream_sink_trait_t<Fun, Finalize>::output> stream_result<typename stream_sink_trait_t<Fun, Finalize>::output>
make_sink(const stream<In>& in, Init init, Fun fun, Finalize finalize, make_sink(const stream<In>& in, Init init, Fun fun, Finalize finalize,
policy::arg<Gatherer> gatherer_type = {}) { policy::arg<Gatherer, Scatterer> policies = {}) {
CAF_IGNORE_UNUSED(gatherer_type); CAF_IGNORE_UNUSED(policies);
CAF_ASSERT(current_mailbox_element() != nullptr);
CAF_ASSERT(current_mailbox_element()->content().match_elements<stream_msg>());
auto& sm = current_mailbox_element()->content().get_as<stream_msg>(0);
CAF_ASSERT(holds_alternative<stream_msg::open>(sm.content));
auto& opn = get<stream_msg::open>(sm.content);
using state_type = typename stream_sink_trait_t<Fun, Finalize>::state; using state_type = typename stream_sink_trait_t<Fun, Finalize>::state;
static_assert(std::is_same< static_assert(std::is_same<
void (state_type&), void (state_type&),
...@@ -557,21 +582,12 @@ public: ...@@ -557,21 +582,12 @@ public:
>::value, >::value,
"Expected signature `void (State&, Input)` " "Expected signature `void (State&, Input)` "
"for consume function"); "for consume function");
auto sid = in.id(); using impl = stream_sink_impl<Fun, Finalize, Gatherer, Scatterer>;
auto next = take_current_next_stage(); auto initializer = [&](impl& x) {
using impl = stream_sink_impl<Fun, Finalize, Gatherer>; init(x.state());
auto ptr = make_counted<impl>(this, std::move(fun), std::move(finalize)); };
auto rp = make_response_promise(); return make_sink_impl<impl>(in, initializer, std::move(fun),
if (!add_source(ptr, sid, std::move(opn.prev_stage), std::move(finalize));
std::move(opn.original_stage), opn.priority,
opn.redeployable, rp)) {
CAF_LOG_ERROR("cannot create stream stage without source");
rp.deliver(sec::cannot_add_upstream);
return none;
}
init(ptr->state());
streams_.emplace(in.id(), ptr);
return {in.id(), std::move(ptr)};
} }
inline streams_map& streams() { inline streams_map& streams() {
......
...@@ -152,6 +152,7 @@ public: ...@@ -152,6 +152,7 @@ public:
using super::remove_path; using super::remove_path;
bool remove_path(path_uptr_iter i, error reason, bool silent) { bool remove_path(path_uptr_iter i, error reason, bool silent) {
CAF_LOG_TRACE(CAF_ARG(reason) << CAF_ARG(silent));
auto e = paths_.end(); auto e = paths_.end();
if (i == e) { if (i == e) {
CAF_LOG_DEBUG("unable to remove path"); CAF_LOG_DEBUG("unable to remove path");
......
...@@ -147,6 +147,13 @@ public: ...@@ -147,6 +147,13 @@ public:
/// safely. /// safely.
virtual bool done() const = 0; virtual bool done() const = 0;
// -- implementation hooks for sources ---------------------------------------
/// Tries to generate new messages for the stream. This member function does
/// nothing on stages and sinks, but can trigger a source to produce more
/// messages.
virtual bool generate_messages();
protected: protected:
// -- implementation hooks for sinks ----------------------------------------- // -- implementation hooks for sinks -----------------------------------------
......
...@@ -219,8 +219,8 @@ typename Inspector::result_type inspect(Inspector& f, stream_msg::open& x) { ...@@ -219,8 +219,8 @@ typename Inspector::result_type inspect(Inspector& f, stream_msg::open& x) {
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, stream_msg::ack_open& x) { typename Inspector::result_type inspect(Inspector& f, stream_msg::ack_open& x) {
return f(meta::type_name("ack_open"), x.rebind_from, x.initial_demand, return f(meta::type_name("ack_open"), x.rebind_from, x.rebind_to,
x.redeployable); x.initial_demand, x.redeployable);
} }
template <class Inspector> template <class Inspector>
...@@ -260,7 +260,7 @@ typename Inspector::result_type inspect(Inspector& f, ...@@ -260,7 +260,7 @@ typename Inspector::result_type inspect(Inspector& f,
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, stream_msg& x) { typename Inspector::result_type inspect(Inspector& f, stream_msg& x) {
return f(meta::type_name("stream_msg"), x.sid, x.content); return f(meta::type_name("stream_msg"), x.sid, x.sender, x.content);
} }
} // namespace caf } // namespace caf
......
...@@ -42,28 +42,55 @@ public: ...@@ -42,28 +42,55 @@ public:
~stream_scatterer_impl() override; ~stream_scatterer_impl() override;
// -- convenience functions for children classes ----------------------------- // -- static utility functions for operating on paths ------------------------
/// Removes all paths gracefully.
void close() override;
/// Removes all paths with an error message. /// Removes all paths with an error message.
void abort(error reason) override; void abort(error reason) override;
/// Returns the total number (sum) of all credit on all paths. /// Folds `paths()` by extracting the `open_credit` from each path.
template <class PathContainer, class F>
static long fold_credit(const PathContainer& xs, long x0, F f) {
auto g = [f](long x, const typename PathContainer::value_type& y) {
return f(x, y->open_credit);
};
return super::fold(xs, x0, std::move(g));
}
/// Returns the total number (sum) of all credit in `xs`.
template <class PathContainer>
static long total_credit(const PathContainer& xs) {
return fold_credit(xs, 0l, [](long x, long y) { return x + y; });
}
/// Returns the minimum number of credit in `xs`.
template <class PathContainer>
static long min_credit(const PathContainer& xs) {
return !xs.empty()
? fold_credit(xs, std::numeric_limits<long>::max(),
[](long x, long y) { return std::min(x, y); })
: 0;
}
template <class PathContainer>
static long max_credit(const PathContainer& xs) {
return fold_credit(xs, 0l, [](long x, long y) { return std::max(x, y); });
}
// -- convenience functions for children classes -----------------------------
/// Returns the total number (sum) of all credit in `paths()`.
long total_credit() const; long total_credit() const;
/// Returns the minimum number of credit on all paths. /// Returns the minimum number of credit in `paths()`.
long min_credit() const; long min_credit() const;
/// Returns the maximum number of credit on all paths. /// Returns the maximum number of credit in `paths()`.
long max_credit() const; long max_credit() const;
/// Folds `paths()` by extracting the `open_credit` from each path.
long fold_credit(long x0, long (*f)(long, long)) const;
// -- overridden functions --------------------------------------------------- // -- overridden functions ---------------------------------------------------
void close() override;
path_ptr add_path(const stream_id& sid, strong_actor_ptr origin, path_ptr add_path(const stream_id& sid, strong_actor_ptr origin,
strong_actor_ptr sink_ptr, strong_actor_ptr sink_ptr,
mailbox_element::forwarding_stack stages, mailbox_element::forwarding_stack stages,
......
...@@ -25,11 +25,10 @@ ...@@ -25,11 +25,10 @@
#include "caf/message_id.hpp" #include "caf/message_id.hpp"
#include "caf/stream_manager.hpp" #include "caf/stream_manager.hpp"
#include "caf/stream_sink_trait.hpp" #include "caf/stream_sink_trait.hpp"
#include "caf/terminal_stream_scatterer.hpp"
namespace caf { namespace caf {
template <class Fun, class Finalize, class UpstreamPolicy> template <class Fun, class Finalize, class Gatherer, class Scatterer>
class stream_sink_impl : public stream_manager { class stream_sink_impl : public stream_manager {
public: public:
using super = stream_manager; using super = stream_manager;
...@@ -53,11 +52,11 @@ public: ...@@ -53,11 +52,11 @@ public:
return state_; return state_;
} }
UpstreamPolicy& in() override { Gatherer& in() override {
return in_; return in_;
} }
terminal_stream_scatterer& out() override { Scatterer& out() override {
return out_; return out_;
} }
...@@ -87,8 +86,8 @@ private: ...@@ -87,8 +86,8 @@ private:
state_type state_; state_type state_;
Fun fun_; Fun fun_;
Finalize fin_; Finalize fin_;
UpstreamPolicy in_; Gatherer in_;
terminal_stream_scatterer out_; Scatterer out_;
}; };
} // namespace caf } // namespace caf
......
...@@ -45,12 +45,6 @@ public: ...@@ -45,12 +45,6 @@ public:
// nop // nop
} }
void generate(size_t num) {
CAF_LOG_TRACE(CAF_ARG(num));
downstream<typename DownstreamPolicy::value_type> ds{out_.buf()};
fun_(state_, ds, num);
}
bool done() const override { bool done() const override {
return at_end() && out_.paths_clean(); return at_end() && out_.paths_clean();
} }
...@@ -63,6 +57,16 @@ public: ...@@ -63,6 +57,16 @@ public:
return out_; return out_;
} }
bool generate_messages() override {
// produce new elements
auto capacity = out_.credit() - out_.buffered();
if (capacity <= 0)
return false;
downstream<typename DownstreamPolicy::value_type> ds{out_.buf()};
fun_(state_, ds, capacity);
return true;
}
state_type& state() { state_type& state() {
return state_; return state_;
} }
...@@ -75,21 +79,8 @@ protected: ...@@ -75,21 +79,8 @@ protected:
void downstream_demand(outbound_path* path, long) override { void downstream_demand(outbound_path* path, long) override {
CAF_LOG_TRACE(CAF_ARG(path)); CAF_LOG_TRACE(CAF_ARG(path));
if (!at_end()) { if (!at_end()) {
// produce new elements generate_messages();
auto capacity = out_.credit();
// TODO: fix issue where a source does not emit the last pieces if
// min_batch_size cannot be reached
while (capacity >= out_.min_batch_size()) {
auto current_size = out_.buffered();
auto size_hint = std::min(capacity, out_.max_batch_size());
if (size_hint > current_size)
generate(static_cast<size_t>(size_hint - current_size));
push(); push();
// Set available credit for next iteration.
auto new_capacity = at_end() ? 0l : out_.credit();
if (new_capacity != capacity)
capacity = new_capacity;
}
} else if (out_.buffered() > 0) { } else if (out_.buffered() > 0) {
push(); push();
} else { } else {
......
...@@ -36,7 +36,7 @@ namespace caf { ...@@ -36,7 +36,7 @@ namespace caf {
/// each lane carries only a subset of the data. For example, the lane /// each lane carries only a subset of the data. For example, the lane
/// mechanism allows you filter key/value pairs before forwarding them to a set /// mechanism allows you filter key/value pairs before forwarding them to a set
/// of workers. /// of workers.
template <class T, class Filter, class KeyCompare, long KeyIndex> template <class T, class Filter, class Select>
class topic_scatterer : public buffered_scatterer<T> { class topic_scatterer : public buffered_scatterer<T> {
public: public:
/// Base type. /// Base type.
...@@ -64,9 +64,11 @@ public: ...@@ -64,9 +64,11 @@ public:
bool remove_path(const stream_id& sid, const actor_addr& x, bool remove_path(const stream_id& sid, const actor_addr& x,
error reason, bool silent) override { error reason, bool silent) override {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(x)
<< CAF_ARG(reason) << CAF_ARG(silent));
auto i = this->iter_find(this->paths_, sid, x); auto i = this->iter_find(this->paths_, sid, x);
if (i != this->paths_.end()) { if (i != this->paths_.end()) {
erase_from_lanes(x); erase_from_lanes(i->get());
return super::remove_path(i, std::move(reason), silent); return super::remove_path(i, std::move(reason), silent);
} }
return false; return false;
...@@ -81,39 +83,41 @@ public: ...@@ -81,39 +83,41 @@ public:
/// @pre `x` is not registered on *any* lane /// @pre `x` is not registered on *any* lane
template <class Handle> template <class Handle>
void set_filter(const stream_id& sid, const Handle& x, filter_type f) { void set_filter(const stream_id& sid, const Handle& x, filter_type f) {
std::sort(f.begin(), f.end()); CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(x) << CAF_ARG(f));
lanes_[std::move(f)].paths.push_back(super::find(sid, x)); auto ptr = super::find(sid, x);
if (!ptr) {
CAF_LOG_WARNING("unable to set filter for unknown path");
return;
} }
erase_from_lanes(ptr);
template <class Handle> lanes_[std::move(f)].paths.push_back(ptr);
void update_filter(const stream_id& sid, const Handle& x, filter_type f) {
std::sort(f.begin(), f.end());
erase_from_lanes(x);
lanes_[std::move(f)].paths.push_back(super::find(sid, x));
} }
const lanes_map& lanes() const { const lanes_map& lanes() const {
return lanes_; return lanes_;
} }
Select& selector() {
return select_;
}
const Select& selector() const {
return select_;
}
protected: protected:
template <class Handle> void erase_from_lanes(typename super::path_ptr ptr) {
void erase_from_lanes(const Handle& x) {
for (auto i = lanes_.begin(); i != lanes_.end(); ++i) for (auto i = lanes_.begin(); i != lanes_.end(); ++i)
if (erase_from_lane(i->second, x)) { if (erase_from_lane(i->second, ptr)) {
if (i->second.paths.empty()) if (i->second.paths.empty())
lanes_.erase(i); lanes_.erase(i);
return; return;
} }
} }
template <class Handle> bool erase_from_lane(lane& l, typename super::path_ptr ptr) {
bool erase_from_lane(lane& l, const Handle& x) {
auto predicate = [&](const outbound_path* y) {
return x == y->hdl;
};
auto e = l.paths.end(); auto e = l.paths.end();
auto i = std::find_if(l.paths.begin(), e, predicate); auto i = std::find(l.paths.begin(), e, ptr);
if (i != e) { if (i != e) {
l.paths.erase(i); l.paths.erase(i);
return true; return true;
...@@ -132,15 +136,13 @@ protected: ...@@ -132,15 +136,13 @@ protected:
/// Returns `true` if `x` is selected by `f`, `false` otherwise. /// Returns `true` if `x` is selected by `f`, `false` otherwise.
bool selected(const filter_type& f, const T& x) { bool selected(const filter_type& f, const T& x) {
using std::get; if (select_(f, x))
for (auto& key : f)
if (cmp_(key, get<KeyIndex>(x)))
return true; return true;
return false; return false;
} }
lanes_map lanes_; lanes_map lanes_;
KeyCompare cmp_; Select select_;
}; };
} // namespace caf } // namespace caf
......
...@@ -36,6 +36,10 @@ ...@@ -36,6 +36,10 @@
namespace caf { namespace caf {
actor::actor(std::nullptr_t) : ptr_(nullptr) {
// nop
}
actor::actor(const scoped_actor& x) : ptr_(actor_cast<strong_actor_ptr>(x)) { actor::actor(const scoped_actor& x) : ptr_(actor_cast<strong_actor_ptr>(x)) {
// nop // nop
} }
...@@ -52,6 +56,11 @@ actor::actor(actor_control_block* ptr, bool add_ref) : ptr_(ptr, add_ref) { ...@@ -52,6 +56,11 @@ actor::actor(actor_control_block* ptr, bool add_ref) : ptr_(ptr, add_ref) {
// nop // nop
} }
actor& actor::operator=(std::nullptr_t) {
ptr_.reset();
return *this;
}
actor& actor::operator=(const scoped_actor& x) { actor& actor::operator=(const scoped_actor& x) {
ptr_ = actor_cast<strong_actor_ptr>(x); ptr_ = actor_cast<strong_actor_ptr>(x);
return *this; return *this;
......
...@@ -99,6 +99,8 @@ actor_system_config::~actor_system_config() { ...@@ -99,6 +99,8 @@ actor_system_config::~actor_system_config() {
actor_system_config::actor_system_config() actor_system_config::actor_system_config()
: cli_helptext_printed(false), : cli_helptext_printed(false),
slave_mode(false), slave_mode(false),
logger_filename(logger_file_name),
logger_filter(logger_component_filter),
slave_mode_fun(nullptr) { slave_mode_fun(nullptr) {
// add `vector<T>` and `stream<T>` for each statically known type // add `vector<T>` and `stream<T>` for each statically known type
add_message_type_impl<stream<actor>>("stream<@actor>"); add_message_type_impl<stream<actor>>("stream<@actor>");
...@@ -216,6 +218,71 @@ actor_system_config::actor_system_config() ...@@ -216,6 +218,71 @@ actor_system_config::actor_system_config()
error_renderers.emplace(atom("exit"), render_exit_reason); error_renderers.emplace(atom("exit"), render_exit_reason);
} }
actor_system_config::actor_system_config(actor_system_config&& other)
: cli_helptext_printed(other.cli_helptext_printed),
slave_mode(other.slave_mode),
slave_name(std::move(other.slave_name)),
bootstrap_node(std::move(other.bootstrap_node)),
args_remainder(std::move(other.args_remainder)),
scheduler_policy(other.scheduler_policy),
scheduler_max_threads(other.scheduler_max_threads),
scheduler_max_throughput(other.scheduler_max_throughput),
scheduler_enable_profiling(std::move(other.scheduler_enable_profiling)),
scheduler_profiling_ms_resolution(
other.scheduler_profiling_ms_resolution),
scheduler_profiling_output_file(other.scheduler_profiling_output_file),
work_stealing_aggressive_poll_attempts(
other.work_stealing_aggressive_poll_attempts),
work_stealing_aggressive_steal_interval(
other.work_stealing_aggressive_steal_interval),
work_stealing_moderate_poll_attempts(
other.work_stealing_moderate_poll_attempts),
work_stealing_moderate_steal_interval(
other.work_stealing_moderate_steal_interval),
work_stealing_moderate_sleep_duration_us(
other.work_stealing_moderate_sleep_duration_us),
work_stealing_relaxed_steal_interval(
other.work_stealing_relaxed_steal_interval),
work_stealing_relaxed_sleep_duration_us(
other.work_stealing_relaxed_sleep_duration_us),
logger_file_name(std::move(other.logger_file_name)),
logger_file_format(std::move(other.logger_file_format)),
logger_console(other.logger_console),
logger_console_format(std::move(other.logger_console_format)),
logger_component_filter(std::move(other.logger_component_filter)),
logger_verbosity(other.logger_verbosity),
logger_inline_output(other.logger_inline_output),
logger_filename(logger_file_name),
logger_filter(logger_component_filter),
middleman_network_backend(other.middleman_network_backend),
middleman_app_identifier(std::move(other.middleman_app_identifier)),
middleman_enable_automatic_connections(
other.middleman_enable_automatic_connections),
middleman_max_consecutive_reads(other.middleman_max_consecutive_reads),
middleman_heartbeat_interval(other.middleman_heartbeat_interval),
middleman_detach_utility_actors(other.middleman_detach_utility_actors),
middleman_detach_multiplexer(other.middleman_detach_multiplexer),
opencl_device_ids(std::move(other.opencl_device_ids)),
openssl_certificate(std::move(other.openssl_certificate)),
openssl_key(std::move(other.openssl_key)),
openssl_passphrase(std::move(other.openssl_passphrase)),
openssl_capath(std::move(other.openssl_capath)),
openssl_cafile(std::move(other.openssl_cafile)),
value_factories_by_name(std::move(other.value_factories_by_name)),
value_factories_by_rtti(std::move(other.value_factories_by_rtti)),
actor_factories(std::move(other.actor_factories)),
module_factories(std::move(other.module_factories)),
hook_factories(std::move(other.hook_factories)),
group_module_factories(std::move(other.group_module_factories)),
type_names_by_rtti(std::move(other.type_names_by_rtti)),
error_renderers(std::move(other.error_renderers)),
named_actor_configs(std::move(other.named_actor_configs)),
slave_mode_fun(other.slave_mode_fun),
custom_options_(std::move(other.custom_options_)),
options_(std::move(other.options_)) {
// nop
}
std::string std::string
actor_system_config::make_help_text(const std::vector<message::cli_arg>& xs) { actor_system_config::make_help_text(const std::vector<message::cli_arg>& xs) {
auto is_no_caf_option = [](const message::cli_arg& arg) { auto is_no_caf_option = [](const message::cli_arg& arg) {
......
...@@ -653,6 +653,10 @@ bool scheduled_actor::handle_stream_msg(mailbox_element& x, ...@@ -653,6 +653,10 @@ bool scheduled_actor::handle_stream_msg(mailbox_element& x,
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
CAF_ASSERT(x.content().match_elements<stream_msg>()); CAF_ASSERT(x.content().match_elements<stream_msg>());
auto& sm = x.content().get_mutable_as<stream_msg>(0); auto& sm = x.content().get_mutable_as<stream_msg>(0);
if (sm.sender == nullptr) {
CAF_LOG_ERROR("received a stream_msg with invalid sender field");
return false;
}
stream_msg_visitor f{this, sm, active_behavior}; stream_msg_visitor f{this, sm, active_behavior};
auto result = visit(f, sm.content); auto result = visit(f, sm.content);
if (streams_.empty() && !has_behavior()) if (streams_.empty() && !has_behavior())
...@@ -660,16 +664,24 @@ bool scheduled_actor::handle_stream_msg(mailbox_element& x, ...@@ -660,16 +664,24 @@ bool scheduled_actor::handle_stream_msg(mailbox_element& x,
return result; return result;
} }
bool scheduled_actor::add_source(const stream_manager_ptr& mgr, const stream_id& sid, bool scheduled_actor::add_source(const stream_manager_ptr& mgr,
strong_actor_ptr source_ptr, strong_actor_ptr original_stage, const stream_id& sid,
strong_actor_ptr source_ptr,
strong_actor_ptr original_stage,
stream_priority prio, bool redeployable, stream_priority prio, bool redeployable,
response_promise result_cb) { response_promise result_cb) {
CAF_LOG_TRACE(CAF_ARG(mgr) << CAF_ARG(sid) << CAF_ARG(source_ptr) CAF_LOG_TRACE(CAF_ARG(mgr) << CAF_ARG(sid) << CAF_ARG(source_ptr)
<< CAF_ARG(original_stage) << CAF_ARG(prio) << CAF_ARG(original_stage) << CAF_ARG(prio)
<< CAF_ARG(redeployable) << CAF_ARG(result_cb)); << CAF_ARG(redeployable) << CAF_ARG(result_cb));
CAF_ASSERT(mgr != nullptr); CAF_ASSERT(mgr != nullptr);
if (!source_ptr || !sid.valid()) if (!source_ptr) {
CAF_LOG_ERROR("cannot add invalid source");
return false;
}
if (!sid.valid()) {
CAF_LOG_ERROR("cannot add source with invalid stream ID");
return false; return false;
}
return mgr->add_source(sid, std::move(source_ptr), return mgr->add_source(sid, std::move(source_ptr),
std::move(original_stage), prio, redeployable, std::move(original_stage), prio, redeployable,
std::move(result_cb)); std::move(result_cb));
...@@ -681,18 +693,22 @@ bool scheduled_actor::add_source(const stream_manager_ptr& mgr, ...@@ -681,18 +693,22 @@ bool scheduled_actor::add_source(const stream_manager_ptr& mgr,
CAF_LOG_TRACE(CAF_ARG(mgr) << CAF_ARG(sid)); CAF_LOG_TRACE(CAF_ARG(mgr) << CAF_ARG(sid));
CAF_ASSERT(mgr != nullptr); CAF_ASSERT(mgr != nullptr);
CAF_ASSERT(current_mailbox_element() != nullptr); CAF_ASSERT(current_mailbox_element() != nullptr);
if (!current_mailbox_element()->content().match_elements<stream_msg>()) if (!current_mailbox_element()->content().match_elements<stream_msg>()) {
CAF_LOG_ERROR("scheduled_actor::add_source called outside "
"a stream_msg handler");
return false; return false;
}
auto& sm = current_mailbox_element()->content().get_mutable_as<stream_msg>(0); auto& sm = current_mailbox_element()->content().get_mutable_as<stream_msg>(0);
if (!holds_alternative<stream_msg::open>(sm.content)) if (!holds_alternative<stream_msg::open>(sm.content)) {
CAF_LOG_ERROR("scheduled_actor::add_source called outside "
"a stream_msg::open handler");
return false; return false;
}
auto& opn = get<stream_msg::open>(sm.content); auto& opn = get<stream_msg::open>(sm.content);
auto source_ptr = std::move(opn.prev_stage); auto source_ptr = std::move(opn.prev_stage);
if (!source_ptr || !sid.valid())
return false;
return mgr->add_source(sid, std::move(source_ptr), return mgr->add_source(sid, std::move(source_ptr),
std::move(opn.original_stage), opn.priority, opn.redeployable, std::move(opn.original_stage), opn.priority,
std::move(result_cb)); opn.redeployable, std::move(result_cb));
} }
} // namespace caf } // namespace caf
...@@ -189,6 +189,10 @@ void stream_manager::push() { ...@@ -189,6 +189,10 @@ void stream_manager::push() {
out().emit_batches(); out().emit_batches();
} }
bool stream_manager::generate_messages() {
return false;
}
message stream_manager::make_final_result() { message stream_manager::make_final_result() {
return none; return none;
} }
......
...@@ -98,23 +98,15 @@ void stream_scatterer_impl::abort(error reason) { ...@@ -98,23 +98,15 @@ void stream_scatterer_impl::abort(error reason) {
} }
long stream_scatterer_impl::total_credit() const { long stream_scatterer_impl::total_credit() const {
return fold_credit(0l, [](long x, long y) { return x + y; }); return total_credit(paths_);
} }
long stream_scatterer_impl::min_credit() const { long stream_scatterer_impl::min_credit() const {
return !paths_.empty() return min_credit(paths_);
? fold_credit(std::numeric_limits<long>::max(),
[](long x, long y) { return std::min(x, y); })
: 0;
} }
long stream_scatterer_impl::max_credit() const { long stream_scatterer_impl::max_credit() const {
return fold_credit(0l, [](long x, long y) { return std::max(x, y); }); return max_credit(paths_);
}
long stream_scatterer_impl::fold_credit(long x0, long (*f)(long, long)) const {
return fold(paths_, x0,
[f](long x, const path_uptr& y) { return f(x, y->open_credit); });
} }
long stream_scatterer_impl::min_batch_size() const { long stream_scatterer_impl::min_batch_size() const {
......
...@@ -31,7 +31,8 @@ stream_scatterer::path_ptr ...@@ -31,7 +31,8 @@ stream_scatterer::path_ptr
terminal_stream_scatterer::add_path(const stream_id&, strong_actor_ptr, terminal_stream_scatterer::add_path(const stream_id&, strong_actor_ptr,
strong_actor_ptr, strong_actor_ptr,
mailbox_element::forwarding_stack, mailbox_element::forwarding_stack,
message_id, message, stream_priority, bool) { message_id, message, stream_priority,
bool) {
CAF_LOG_ERROR("terminal_stream_scatterer::add_path called"); CAF_LOG_ERROR("terminal_stream_scatterer::add_path called");
return nullptr; return nullptr;
} }
...@@ -85,8 +86,8 @@ void terminal_stream_scatterer::emit_batches() { ...@@ -85,8 +86,8 @@ void terminal_stream_scatterer::emit_batches() {
// nop // nop
} }
stream_scatterer::path_type* terminal_stream_scatterer::find(const stream_id&, stream_scatterer::path_type*
const actor_addr&) { terminal_stream_scatterer::find(const stream_id&, const actor_addr&) {
return nullptr; return nullptr;
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::vector;
using std::string; using std::string;
using namespace caf; using namespace caf;
...@@ -66,10 +67,20 @@ struct cleanup_t { ...@@ -66,10 +67,20 @@ struct cleanup_t {
constexpr cleanup_t cleanup_fun = cleanup_t{}; constexpr cleanup_t cleanup_fun = cleanup_t{};
struct selected_t {
template <class T>
bool operator()(const std::vector<string>& filter, const T& x) {
for (auto& prefix : filter)
if (get<0>(x).compare(0, prefix.size(), prefix.c_str()) == 0)
return true;
return false;
}
};
struct stream_splitter_state { struct stream_splitter_state {
using stage_impl = stream_stage_impl< using stage_impl = stream_stage_impl<
process_t, cleanup_t, random_gatherer, process_t, cleanup_t, random_gatherer,
random_topic_scatterer<element_type, std::vector<key_type>>>; random_topic_scatterer<element_type, std::vector<key_type>, selected_t>>;
intrusive_ptr<stage_impl> stage; intrusive_ptr<stage_impl> stage;
static const char* name; static const char* name;
}; };
...@@ -137,7 +148,7 @@ behavior storage(stateful_actor<storage_state>* self, ...@@ -137,7 +148,7 @@ behavior storage(stateful_actor<storage_state>* self,
[](unit_t&) { [](unit_t&) {
CAF_LOG_INFO("storage done"); CAF_LOG_INFO("storage done");
}, },
policy::arg<detail::pull5_gatherer>::value policy::arg<detail::pull5_gatherer, terminal_stream_scatterer>::value
); );
}, },
[=](get_atom) { [=](get_atom) {
...@@ -206,7 +217,7 @@ CAF_TEST(fork_setup) { ...@@ -206,7 +217,7 @@ CAF_TEST(fork_setup) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(d1).with(_, splitter, _, _, _, false)); from(_).to(d1).with(_, splitter, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(d1).to(splitter).with(_, 5, _, false)); from(d1).to(splitter).with(_, _, 5, _, false));
CAF_MESSAGE("spawn second sink"); CAF_MESSAGE("spawn second sink");
auto d2 = sys.spawn(storage, splitter, filter_type{"key2"}); auto d2 = sys.spawn(storage, splitter, filter_type{"key2"});
sched.run_once(); sched.run_once();
...@@ -215,7 +226,7 @@ CAF_TEST(fork_setup) { ...@@ -215,7 +226,7 @@ CAF_TEST(fork_setup) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(d2).with(_, splitter, _, _, _, false)); from(_).to(d2).with(_, splitter, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(d2).to(splitter).with(_, 5, _, false)); from(d2).to(splitter).with(_, _, 5, _, false));
CAF_MESSAGE("spawn source"); CAF_MESSAGE("spawn source");
auto src = sys.spawn(nores_streamer, splitter); auto src = sys.spawn(nores_streamer, splitter);
sched.run_once(); sched.run_once();
...@@ -223,7 +234,7 @@ CAF_TEST(fork_setup) { ...@@ -223,7 +234,7 @@ CAF_TEST(fork_setup) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(splitter).with(_, src, _, _, _, false)); from(_).to(splitter).with(_, src, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(splitter).to(src).with(_, 5, _, false)); from(splitter).to(src).with(_, _, 5, _, false));
// First batch. // First batch.
expect((stream_msg::batch), expect((stream_msg::batch),
from(src).to(splitter) from(src).to(splitter)
...@@ -281,7 +292,7 @@ CAF_TEST(fork_setup) { ...@@ -281,7 +292,7 @@ CAF_TEST(fork_setup) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(splitter).with(_, src2, _, _, _, false)); from(_).to(splitter).with(_, src2, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(splitter).to(src2).with(_, 5, _, false)); from(splitter).to(src2).with(_, _, 5, _, false));
// First batch. // First batch.
expect((stream_msg::batch), expect((stream_msg::batch),
from(src2).to(splitter) from(src2).to(splitter)
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2017 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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 <vector>
#include <string>
#include <numeric>
#include <utility>
#include <fstream>
#include <iostream>
#include <iterator>
#define CAF_SUITE stream_distribution_tree
#include "caf/test/dsl.hpp"
#include "caf/fused_scatterer.hpp"
#include "caf/broadcast_topic_scatterer.hpp"
#include "caf/detail/pull5_gatherer.hpp"
#include "caf/detail/push5_scatterer.hpp"
#include "caf/detail/stream_distribution_tree.hpp"
using std::cout;
using std::endl;
using std::vector;
using std::string;
using namespace caf::detail;
using namespace caf;
namespace {
using join_atom = atom_constant<atom("join")>;
using peer_atom = atom_constant<atom("peer")>;
struct prefix_match_t {
using topic_type = string;
using filter_type = vector<string>;
bool operator()(const filter_type& filter,
const topic_type& topic) const {
for (auto& prefix : filter)
if (topic.compare(0, prefix.size(), prefix.c_str()) == 0)
return true;
return false;
}
template <class T>
bool operator()(const filter_type& filter,
const std::pair<topic_type, T>& x) const {
return (*this)(filter, x.first);
}
bool operator()(const filter_type& filter, const message& msg) const {
if (!msg.match_element<topic_type>(0))
return false;
return (*this)(filter, msg.get_as<topic_type>(0));
}
};
constexpr prefix_match_t prefix_match = prefix_match_t{};
using peer_filter_type = std::pair<actor_addr, vector<string>>;
struct peer_filter_cmp {
actor_addr active_sender;
template <class T>
bool operator()(const peer_filter_type& f, const T& x) const {
return f.first != active_sender && prefix_match(f.second, x);
}
};
class policy {
public:
using data = int;
using internal_command = string;
using topic = string;
using filter_type = vector<string>;
using gatherer_type = detail::pull5_gatherer;
using peer_batch = std::vector<message>;
using worker_batch = std::vector<std::pair<topic, data>>;
using store_batch = std::vector<std::pair<topic, internal_command>>;
using path_id = std::pair<stream_id, actor_addr>;
using peer_to_path_map = std::map<actor, path_id>;
using path_to_peer_map = std::map<path_id, actor>;
template <class T>
using substream_t = broadcast_topic_scatterer<std::pair<topic, T>,
filter_type, prefix_match_t>;
using main_stream_t = broadcast_topic_scatterer<message, peer_filter_type,
peer_filter_cmp>;
using scatterer_type = fused_scatterer<main_stream_t, substream_t<data>,
substream_t<internal_command>>;
policy(stream_distribution_tree<policy>* parent, filter_type filter);
/// Returns true if 1) `shutting_down()`, 2) there is no more
/// active local data source, and 3) there is no pending data to any peer.
bool at_end() const {
return shutting_down_ && peers().paths_clean()
&& workers().paths_clean() && stores().paths_clean();
}
bool substream_local_data() const {
return false;
}
void before_handle_batch(const stream_id&, const actor_addr& hdl,
long, message&, int64_t) {
parent_->out().main_stream().selector().active_sender = hdl;
}
void handle_batch(message& xs) {
if (xs.match_elements<peer_batch>()) {
// Only received from other peers. Extract content for to local workers
// or stores and then forward to other peers.
for (auto& msg : xs.get_mutable_as<peer_batch>(0)) {
// Extract worker messages.
if (msg.match_elements<topic, data>())
workers().push(msg.get_as<topic>(0), msg.get_as<data>(1));
// Extract store messages.
if (msg.match_elements<topic, internal_command>())
stores().push(msg.get_as<topic>(0), msg.get_as<internal_command>(1));
// Forward to other peers.
peers().push(std::move(msg));
}
} else if (xs.match_elements<worker_batch>()) {
// Inputs from local workers are only forwarded to peers.
for (auto& x : xs.get_mutable_as<worker_batch>(0)) {
parent_->out().push(make_message(std::move(x.first),
std::move(x.second)));
}
} else if (xs.match_elements<store_batch>()) {
// Inputs from stores are only forwarded to peers.
for (auto& x : xs.get_mutable_as<store_batch>(0)) {
parent_->out().push(make_message(std::move(x.first),
std::move(x.second)));
}
} else {
CAF_LOG_ERROR("unexpected batch:" << deep_to_string(xs));
}
}
void after_handle_batch(const stream_id&, const actor_addr&, int64_t) {
parent_->out().main_stream().selector().active_sender = nullptr;
}
void ack_open_success(const stream_id& sid, const actor_addr& rebind_from,
strong_actor_ptr rebind_to) {
auto old_id = std::make_pair(sid, rebind_from);
auto new_id = std::make_pair(sid, actor_cast<actor_addr>(rebind_to));
auto i = opath_to_peer_.find(old_id);
if (i != opath_to_peer_.end()) {
auto pp = std::move(i->second);
peer_to_opath_[pp] = new_id;
opath_to_peer_.erase(i);
opath_to_peer_.emplace(new_id, std::move(pp));
}
}
void ack_open_failure(const stream_id& sid, const actor_addr& rebind_from,
strong_actor_ptr rebind_to, const error&) {
auto old_id = std::make_pair(sid, rebind_from);
auto new_id = std::make_pair(sid, actor_cast<actor_addr>(rebind_to));
auto i = opath_to_peer_.find(old_id);
if (i != opath_to_peer_.end()) {
auto pp = std::move(i->second);
peer_lost(pp);
peer_to_opath_.erase(pp);
opath_to_peer_.erase(i);
}
}
void push_to_substreams(vector<message> vec) {
CAF_LOG_TRACE(CAF_ARG(vec));
// Move elements from `xs` to the buffer for local subscribers.
if (!workers().lanes().empty())
for (auto& x : vec)
if (x.match_elements<topic, data>()) {
x.force_unshare();
workers().push(x.get_as<topic>(0),
std::move(x.get_mutable_as<data>(1)));
}
workers().emit_batches();
if (!stores().lanes().empty())
for (auto& x : vec)
if (x.match_elements<topic, internal_command>()) {
x.force_unshare();
stores().push(x.get_as<topic>(0),
std::move(x.get_mutable_as<internal_command>(1)));
}
stores().emit_batches();
}
optional<error> batch(const stream_id&, const actor_addr&,
long, message& xs, int64_t) {
if (xs.match_elements<std::vector<std::pair<topic, data>>>()) {
return error{none};
}
if (xs.match_elements<std::vector<std::pair<topic, internal_command>>>()) {
return error{none};
}
return none;
}
// -- callbacks --------------------------------------------------------------
void peer_lost(const actor&) {
// nop
}
void local_input_closed(const stream_id&, const actor_addr&) {
// nop
}
// -- state required by the distribution tree --------------------------------
bool shutting_down() {
return shutting_down_;
}
void shutting_down(bool value) {
shutting_down_ = value;
}
// -- peer management --------------------------------------------------------
/// Adds a new peer that isn't fully initialized yet. A peer is fully
/// initialized if there is an upstream ID associated to it.
bool add_peer(const caf::stream_id& sid,
const strong_actor_ptr& downstream_handle,
const actor& peer_handle, filter_type filter) {
CAF_LOG_TRACE(CAF_ARG(sid) << CAF_ARG(downstream_handle)
<< CAF_ARG(peer_handle) << CAF_ARG(filter));
//TODO: auto ptr = peers().add_path(sid, downstream_handle);
outbound_path* ptr = nullptr;
if (ptr == nullptr)
return false;
auto downstream_addr = actor_cast<actor_addr>(downstream_handle);
peers().set_filter(sid, downstream_handle,
{downstream_addr, std::move(filter)});
peer_to_opath_.emplace(peer_handle, std::make_pair(sid, downstream_addr));
opath_to_peer_.emplace(std::make_pair(sid, downstream_addr), peer_handle);
return true;
}
/// Fully initializes a peer by setting an upstream ID and inserting it into
/// the `input_to_peer_` map.
bool init_peer(const stream_id& sid, const strong_actor_ptr& upstream_handle,
const actor& peer_handle) {
auto upstream_addr = actor_cast<actor_addr>(upstream_handle);
peer_to_ipath_.emplace(peer_handle, std::make_pair(sid, upstream_addr));
ipath_to_peer_.emplace(std::make_pair(sid, upstream_addr), peer_handle);
return true;
}
/// Removes a peer, aborting any stream to & from that peer.
bool remove_peer(const actor& hdl, error reason, bool silent) {
CAF_LOG_TRACE(CAF_ARG(hdl));
{ // lifetime scope of first iterator pair
auto e = peer_to_opath_.end();
auto i = peer_to_opath_.find(hdl);
if (i == e)
return false;
peers().remove_path(i->second.first, i->second.second, reason, silent);
opath_to_peer_.erase(std::make_pair(i->second.first, i->second.second));
peer_to_opath_.erase(i);
}
{ // lifetime scope of second iterator pair
auto e = peer_to_ipath_.end();
auto i = peer_to_ipath_.find(hdl);
CAF_IGNORE_UNUSED(e);
CAF_ASSERT(i != e);
parent_->in().remove_path(i->second.first, i->second.second,
reason, silent);
ipath_to_peer_.erase(std::make_pair(i->second.first, i->second.second));
peer_to_ipath_.erase(i);
}
peer_lost(hdl);
if (shutting_down() && peer_to_opath_.empty()) {
// Shutdown when the last peer stops listening.
parent_->self()->quit(caf::exit_reason::user_shutdown);
} else {
// See whether we can make progress without that peer in the mix.
parent_->in().assign_credit(parent_->out().credit());
parent_->push();
}
return true;
}
/// Updates the filter of an existing peer.
bool update_peer(const actor& hdl, filter_type filter) {
CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(filter));
auto e = peer_to_opath_.end();
auto i = peer_to_opath_.find(hdl);
if (i == e) {
CAF_LOG_DEBUG("cannot update filter on unknown peer");
return false;
}
peers().set_filter(i->second.first, i->second.second,
{i->second.second, std::move(filter)});
return true;
}
// -- selectively pushing data into the streams ------------------------------
/// Pushes data to workers without forwarding it to peers.
void local_push(topic x, data y) {
workers().push(std::move(x), std::move(y));
workers().emit_batches();
}
/// Pushes data to stores without forwarding it to peers.
void local_push(topic x, internal_command y) {
stores().push(std::move(x), std::move(y));
stores().emit_batches();
}
/// Pushes data to peers only without forwarding it to local substreams.
void remote_push(message msg) {
peers().push(std::move(msg));
peers().emit_batches();
}
/// Pushes data to peers and workers.
void push(topic x, data y) {
remote_push(make_message(x, y));
local_push(std::move(x), std::move(y));
}
/// Pushes data to peers and stores.
void push(topic x, internal_command y) {
remote_push(make_message(x, y));
local_push(std::move(x), std::move(y));
}
// -- state accessors --------------------------------------------------------
main_stream_t& peers() {
return parent_->out().main_stream();
}
const main_stream_t& peers() const {
return parent_->out().main_stream();
}
substream_t<data>& workers() {
return parent_->out().substream<1>();
}
const substream_t<data>& workers() const {
return parent_->out().substream<1>();
}
substream_t<internal_command>& stores() {
return parent_->out().substream<2>();
}
const substream_t<internal_command>& stores() const {
return parent_->out().substream<2>();
}
private:
stream_distribution_tree<policy>* parent_;
bool shutting_down_;
// Maps peer handles to output path IDs.
peer_to_path_map peer_to_opath_;
// Maps output path IDs to peer handles.
path_to_peer_map opath_to_peer_;
// Maps peer handles to input path IDs.
peer_to_path_map peer_to_ipath_;
// Maps input path IDs to peer handles.
path_to_peer_map ipath_to_peer_;
};
policy::policy(stream_distribution_tree<policy>* parent, filter_type)
: parent_(parent),
shutting_down_(false) {
// nop
}
policy::filter_type default_filter() {
return {"foo", "bar"};
}
/*
behavior testee(event_based_actor* self) {
auto sdt = make_counted<stream_distribution_tree<policy>>(self,
default_filter());
return {
};
}
*/
using fixture = test_coordinator_fixture<>;
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(stream_distribution_tree_tests, fixture)
CAF_TEST(peer_management) {
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -183,7 +183,7 @@ behavior sum_up(stateful_actor<sum_up_state>* self) { ...@@ -183,7 +183,7 @@ behavior sum_up(stateful_actor<sum_up_state>* self) {
[](int& x) -> int { [](int& x) -> int {
return x; return x;
}, },
policy::arg<detail::pull5_gatherer>::value policy::arg<detail::pull5_gatherer, terminal_stream_scatterer>::value
); );
} }
}; };
...@@ -214,7 +214,7 @@ behavior drop_all(stateful_actor<drop_all_state>* self) { ...@@ -214,7 +214,7 @@ behavior drop_all(stateful_actor<drop_all_state>* self) {
[](unit_t&) { [](unit_t&) {
CAF_LOG_INFO("drop_all done"); CAF_LOG_INFO("drop_all done");
}, },
policy::arg<detail::pull5_gatherer>::value policy::arg<detail::pull5_gatherer, terminal_stream_scatterer>::value
); );
} }
}; };
...@@ -380,7 +380,7 @@ CAF_TEST(depth2_pipeline) { ...@@ -380,7 +380,7 @@ CAF_TEST(depth2_pipeline) {
expect((stream_msg::open), expect((stream_msg::open),
from(self).to(sink).with(_, source, _, _, _, _, false)); from(self).to(sink).with(_, source, _, _, _, _, false));
// source <----(stream_msg::ack_open)------ sink // source <----(stream_msg::ack_open)------ sink
expect((stream_msg::ack_open), from(sink).to(source).with(_, 5, _, false)); expect((stream_msg::ack_open), from(sink).to(source).with(_, _, 5, _, false));
// source ----(stream_msg::batch)---> sink // source ----(stream_msg::batch)---> sink
expect((stream_msg::batch), expect((stream_msg::batch),
from(source).to(sink).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(source).to(sink).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
...@@ -429,9 +429,10 @@ CAF_TEST(depth3_pipeline_order1) { ...@@ -429,9 +429,10 @@ CAF_TEST(depth3_pipeline_order1) {
CAF_CHECK(!deref(stage).streams().empty()); CAF_CHECK(!deref(stage).streams().empty());
CAF_CHECK(!deref(sink).streams().empty()); CAF_CHECK(!deref(sink).streams().empty());
// sink --(stream_msg::ack_open)--> stage // sink --(stream_msg::ack_open)--> stage
expect((stream_msg::ack_open), from(sink).to(stage).with(_, 5, _, false)); expect((stream_msg::ack_open), from(sink).to(stage).with(_, _, 5, _, false));
// stage --(stream_msg::ack_open)--> source // stage --(stream_msg::ack_open)--> source
expect((stream_msg::ack_open), from(stage).to(source).with(_, 5, _, false)); expect((stream_msg::ack_open),
from(stage).to(source).with(_, _, 5, _, false));
// source --(stream_msg::batch)--> stage // source --(stream_msg::batch)--> stage
expect((stream_msg::batch), expect((stream_msg::batch),
from(source).to(stage).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(source).to(stage).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
...@@ -483,7 +484,8 @@ CAF_TEST(depth3_pipeline_order2) { ...@@ -483,7 +484,8 @@ CAF_TEST(depth3_pipeline_order2) {
expect((stream_msg::open), expect((stream_msg::open),
from(self).to(stage).with(_, source, _, _, _, false)); from(self).to(stage).with(_, source, _, _, _, false));
// stage --(stream_msg::ack_open)--> source // stage --(stream_msg::ack_open)--> source
expect((stream_msg::ack_open), from(stage).to(source).with(_, 5, _, false)); expect((stream_msg::ack_open),
from(stage).to(source).with(_, _, 5, _, false));
// source --(stream_msg::batch)--> stage // source --(stream_msg::batch)--> stage
expect((stream_msg::batch), expect((stream_msg::batch),
from(source).to(stage).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(source).to(stage).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
...@@ -516,7 +518,7 @@ CAF_TEST(depth3_pipeline_order2) { ...@@ -516,7 +518,7 @@ CAF_TEST(depth3_pipeline_order2) {
expect((stream_msg::open), expect((stream_msg::open),
from(self).to(sink).with(_, stage, _, _, _, false)); from(self).to(sink).with(_, stage, _, _, _, false));
// sink --(stream_msg::ack_open)--> stage (finally) // sink --(stream_msg::ack_open)--> stage (finally)
expect((stream_msg::ack_open), from(sink).to(stage).with(_, 5, _, false)); expect((stream_msg::ack_open), from(sink).to(stage).with(_, _, 5, _, false));
// stage --(stream_msg::ack_batch)--> source // stage --(stream_msg::ack_batch)--> source
// The stage has now emptied its buffer and is able to grant more credit. // The stage has now emptied its buffer and is able to grant more credit.
expect((stream_msg::ack_batch), from(stage).to(source).with(5, 3)); expect((stream_msg::ack_batch), from(stage).to(source).with(5, 3));
...@@ -570,7 +572,7 @@ CAF_TEST(depth2_pipeline_streamer) { ...@@ -570,7 +572,7 @@ CAF_TEST(depth2_pipeline_streamer) {
expect((stream_msg::open), expect((stream_msg::open),
from(source).to(sink).with(_, source, _, _, _, false)); from(source).to(sink).with(_, source, _, _, _, false));
// source <----(stream_msg::ack_open)------ sink // source <----(stream_msg::ack_open)------ sink
expect((stream_msg::ack_open), from(sink).to(source).with(_, 5, _, false)); expect((stream_msg::ack_open), from(sink).to(source).with(_, _, 5, _, false));
// source ----(stream_msg::batch)---> sink // source ----(stream_msg::batch)---> sink
expect((stream_msg::batch), expect((stream_msg::batch),
from(source).to(sink).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(source).to(sink).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
...@@ -598,7 +600,7 @@ CAF_TEST(stream_without_result) { ...@@ -598,7 +600,7 @@ CAF_TEST(stream_without_result) {
expect((stream_msg::open), expect((stream_msg::open),
from(source).to(sink).with(_, source, _, _, _, false)); from(source).to(sink).with(_, source, _, _, _, false));
// source <----(stream_msg::ack_open)------ sink // source <----(stream_msg::ack_open)------ sink
expect((stream_msg::ack_open), from(sink).to(source).with(_, 5, _, false)); expect((stream_msg::ack_open), from(sink).to(source).with(_, _, 5, _, false));
// source ----(stream_msg::batch)---> sink // source ----(stream_msg::batch)---> sink
expect((stream_msg::batch), expect((stream_msg::batch),
from(source).to(sink).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(source).to(sink).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
...@@ -629,7 +631,7 @@ CAF_TEST(multiplexed_pipeline) { ...@@ -629,7 +631,7 @@ CAF_TEST(multiplexed_pipeline) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(d1).with(_, multiplexer, _, _, _, false)); from(_).to(d1).with(_, multiplexer, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(d1).to(multiplexer).with(_, 5, _, false)); from(d1).to(multiplexer).with(_, _, 5, _, false));
CAF_MESSAGE("spawn second sink"); CAF_MESSAGE("spawn second sink");
auto d2 = sys.spawn(joining_drop_all); auto d2 = sys.spawn(joining_drop_all);
sched.run_once(); sched.run_once();
...@@ -637,7 +639,7 @@ CAF_TEST(multiplexed_pipeline) { ...@@ -637,7 +639,7 @@ CAF_TEST(multiplexed_pipeline) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(d2).with(_, multiplexer, _, _, _, false)); from(_).to(d2).with(_, multiplexer, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(d2).to(multiplexer).with(_, 5, _, false)); from(d2).to(multiplexer).with(_, _, 5, _, false));
CAF_MESSAGE("spawn source"); CAF_MESSAGE("spawn source");
auto src = sys.spawn(nores_streamer, multiplexer); auto src = sys.spawn(nores_streamer, multiplexer);
sched.run_once(); sched.run_once();
...@@ -645,7 +647,7 @@ CAF_TEST(multiplexed_pipeline) { ...@@ -645,7 +647,7 @@ CAF_TEST(multiplexed_pipeline) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(multiplexer).with(_, src, _, _, _, false)); from(_).to(multiplexer).with(_, src, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(multiplexer).to(src).with(_, 5, _, false)); from(multiplexer).to(src).with(_, _, 5, _, false));
// First batch. // First batch.
expect((stream_msg::batch), expect((stream_msg::batch),
from(src).to(multiplexer).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(src).to(multiplexer).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
...@@ -676,7 +678,7 @@ CAF_TEST(multiplexed_pipeline) { ...@@ -676,7 +678,7 @@ CAF_TEST(multiplexed_pipeline) {
expect((stream_msg::open), expect((stream_msg::open),
from(_).to(multiplexer).with(_, src2, _, _, _, false)); from(_).to(multiplexer).with(_, src2, _, _, _, false));
expect((stream_msg::ack_open), expect((stream_msg::ack_open),
from(multiplexer).to(src2).with(_, 5, _, false)); from(multiplexer).to(src2).with(_, _, 5, _, false));
// First batch. // First batch.
expect((stream_msg::batch), expect((stream_msg::batch),
from(src2).to(multiplexer).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0)); from(src2).to(multiplexer).with(5, std::vector<int>{1, 2, 3, 4, 5}, 0));
......
...@@ -58,7 +58,7 @@ namespace caf { ...@@ -58,7 +58,7 @@ namespace caf {
template <class... Ts> template <class... Ts>
bool operator==(const message& x, const std::tuple<Ts...>& y) { bool operator==(const message& x, const std::tuple<Ts...>& y) {
return msg_cmp_rec<0>(x, y); return x.size() == sizeof...(Ts) && msg_cmp_rec<0>(x, y);
} }
template <class T> template <class T>
......
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