316 lines
8.4 KiB
C++
316 lines
8.4 KiB
C++
#pragma once
|
|
|
|
#include "broker/data.hh"
|
|
#include "broker/fwd.hh"
|
|
#include "broker/message.hh"
|
|
#include "broker/time.hh"
|
|
|
|
#include <caf/type_id.hpp>
|
|
#include <caf/uuid.hpp>
|
|
|
|
#include <random>
|
|
|
|
// -- forward declarations -----------------------------------------------------
|
|
|
|
struct legacy_node_message;
|
|
|
|
using uuid = caf::uuid;
|
|
|
|
struct uuid_multipath_tree;
|
|
|
|
class uuid_multipath;
|
|
class uuid_multipath_group;
|
|
class uuid_multipath_node;
|
|
|
|
using node_message_content =
|
|
broker::variant<broker::data_message, broker::command_message>;
|
|
|
|
using uuid_node_message = caf::cow_tuple<node_message_content, uuid_multipath>;
|
|
|
|
// -- type IDs -----------------------------------------------------------------
|
|
|
|
#define MICRO_BENCH_ADD_TYPE(type) CAF_ADD_TYPE_ID(micro_benchmarks, type)
|
|
|
|
CAF_BEGIN_TYPE_ID_BLOCK(micro_benchmarks, caf::id_block::broker_internal::end)
|
|
|
|
MICRO_BENCH_ADD_TYPE((caf::stream<legacy_node_message>) )
|
|
MICRO_BENCH_ADD_TYPE((legacy_node_message))
|
|
MICRO_BENCH_ADD_TYPE((std::vector<legacy_node_message>) )
|
|
|
|
MICRO_BENCH_ADD_TYPE((caf::stream<uuid_node_message>) )
|
|
MICRO_BENCH_ADD_TYPE((std::vector<uuid_node_message>) )
|
|
MICRO_BENCH_ADD_TYPE((uuid_multipath))
|
|
MICRO_BENCH_ADD_TYPE((uuid_node_message))
|
|
|
|
CAF_END_TYPE_ID_BLOCK(micro_benchmarks)
|
|
|
|
// -- custom types -------------------------------------------------------------
|
|
|
|
/// A `node_message` as it used to be pre-ALM.
|
|
struct legacy_node_message {
|
|
/// Content of the message.
|
|
node_message_content content;
|
|
|
|
/// Time-to-life counter.
|
|
uint16_t ttl;
|
|
};
|
|
|
|
template <class Inspector>
|
|
bool inspect(Inspector& f, legacy_node_message& x) {
|
|
return f.object(x).fields(f.field("content", x.content),
|
|
f.field("ttl", x.ttl));
|
|
}
|
|
|
|
using uuid = caf::uuid;
|
|
|
|
class uuid_multipath_node;
|
|
|
|
struct uuid_multipath_tree {
|
|
uuid_multipath_tree(uuid id, bool is_receiver);
|
|
~uuid_multipath_tree();
|
|
uuid_multipath_node* root;
|
|
broker::detail::monotonic_buffer_resource mem;
|
|
};
|
|
|
|
class uuid_multipath_group {
|
|
public:
|
|
friend class uuid_multipath;
|
|
friend class uuid_multipath_node;
|
|
|
|
using iterator = broker::alm::node_iterator<uuid_multipath_node>;
|
|
|
|
using const_iterator = broker::alm::node_iterator<const uuid_multipath_node>;
|
|
|
|
uuid_multipath_group() noexcept = default;
|
|
|
|
uuid_multipath_group(const uuid_multipath_group&) = delete;
|
|
|
|
uuid_multipath_group& operator=(const uuid_multipath_group&) = delete;
|
|
|
|
~uuid_multipath_group();
|
|
|
|
size_t size() const noexcept {
|
|
return size_;
|
|
}
|
|
|
|
bool empty() const noexcept {
|
|
return size_ == 0;
|
|
}
|
|
|
|
iterator begin() noexcept {
|
|
return iterator{first_};
|
|
}
|
|
|
|
const_iterator begin() const noexcept {
|
|
return const_iterator{first_};
|
|
}
|
|
|
|
iterator end() noexcept {
|
|
return iterator{nullptr};
|
|
}
|
|
|
|
const_iterator end() const noexcept {
|
|
return const_iterator{nullptr};
|
|
}
|
|
|
|
bool equals(const uuid_multipath_group& other) const noexcept;
|
|
|
|
bool contains(uuid what) const noexcept;
|
|
|
|
std::pair<uuid_multipath_node*, bool>
|
|
emplace(broker::detail::monotonic_buffer_resource& mem, uuid id,
|
|
bool is_receiver);
|
|
|
|
bool emplace(uuid_multipath_node* node);
|
|
|
|
private:
|
|
template <class MakeNewNode>
|
|
std::pair<uuid_multipath_node*, bool> emplace_impl(uuid id,
|
|
MakeNewNode make_new_node);
|
|
|
|
size_t size_ = 0;
|
|
uuid_multipath_node* first_ = nullptr;
|
|
};
|
|
|
|
class uuid_multipath_node {
|
|
public:
|
|
friend class uuid_multipath;
|
|
friend class uuid_multipath_group;
|
|
friend class broker::alm::node_iterator<const uuid_multipath_node>;
|
|
friend class broker::alm::node_iterator<uuid_multipath_node>;
|
|
friend struct uuid_multipath_tree;
|
|
|
|
uuid_multipath_node(uuid id, bool is_receiver) noexcept
|
|
: id_(id), is_receiver_(is_receiver) {
|
|
// nop
|
|
}
|
|
|
|
uuid_multipath_node() = delete;
|
|
|
|
uuid_multipath_node(const uuid_multipath_node&) = delete;
|
|
|
|
uuid_multipath_node& operator=(const uuid_multipath_node&) = delete;
|
|
|
|
~uuid_multipath_node();
|
|
|
|
const uuid& id() const noexcept {
|
|
return id_;
|
|
}
|
|
|
|
bool is_receiver() const noexcept {
|
|
return is_receiver_;
|
|
}
|
|
|
|
auto& nodes() noexcept {
|
|
return down_;
|
|
}
|
|
|
|
const auto& nodes() const noexcept {
|
|
return down_;
|
|
}
|
|
|
|
bool equals(const uuid_multipath_node& other) const noexcept;
|
|
|
|
bool contains(uuid what) const noexcept;
|
|
|
|
private:
|
|
template <class Inspector>
|
|
bool save_children(Inspector& f) {
|
|
if (f.begin_sequence(down_.size()))
|
|
for (auto& child : down_)
|
|
if (!child.save(f))
|
|
return false;
|
|
return f.end_sequence();
|
|
}
|
|
|
|
template <class Inspector>
|
|
bool save(Inspector& f) {
|
|
// We are lying to the inspector about the type, because multipath_node and
|
|
// multipath_group are internal implementation details.
|
|
return f.begin_object(caf::type_id_v<uuid_multipath>,
|
|
caf::type_name_v<uuid_multipath>)
|
|
&& f.begin_field("id") // <id>
|
|
&& f.apply(id_) // [...]
|
|
&& f.end_field() // </id>
|
|
&& f.begin_field("is_receiver") // <is_receiver>
|
|
&& f.apply(is_receiver_) // [...]
|
|
&& f.end_field() // </is_receiver>
|
|
&& f.begin_field("nodes") // <nodes>
|
|
&& save_children(f) // [...]
|
|
&& f.end_field() // </nodes>
|
|
&& f.end_object();
|
|
}
|
|
|
|
template <class Inspector>
|
|
bool load_children(broker::detail::monotonic_buffer_resource& mem,
|
|
Inspector& f) {
|
|
size_t n = 0;
|
|
if (f.begin_sequence(n)) {
|
|
for (size_t i = 0; i < n; ++i) {
|
|
auto child =
|
|
broker::detail::new_instance<uuid_multipath_node>(mem, uuid{}, false);
|
|
if (!child->load(mem, f)) {
|
|
return false;
|
|
} else if (!down_.emplace(child)) {
|
|
f.emplace_error(caf::sec::field_invariant_check_failed,
|
|
"a multipath may not contain duplicates");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return f.end_sequence();
|
|
}
|
|
|
|
template <class Inspector>
|
|
bool load(broker::detail::monotonic_buffer_resource& mem, Inspector& f) {
|
|
return f.begin_object(caf::type_id_v<uuid_multipath>,
|
|
caf::type_name_v<uuid_multipath>)
|
|
&& f.begin_field("id") // <id>
|
|
&& f.apply(id_) // [...]
|
|
&& f.end_field() // </id>
|
|
&& f.begin_field("is_receiver") // <is_receiver>
|
|
&& f.apply(is_receiver_) // [...]
|
|
&& f.end_field() // </is_receiver>
|
|
&& f.begin_field("nodes") // <nodes>
|
|
&& load_children(mem, f) // [...]
|
|
&& f.end_field() // </nodes>
|
|
&& f.end_object();
|
|
}
|
|
|
|
uuid id_;
|
|
bool is_receiver_;
|
|
uuid_multipath_node* right_ = nullptr;
|
|
uuid_multipath_group down_;
|
|
};
|
|
|
|
class uuid_multipath {
|
|
public:
|
|
using tree_ptr = std::shared_ptr<uuid_multipath_tree>;
|
|
|
|
uuid_multipath();
|
|
|
|
uuid_multipath(uuid id, bool is_receiver);
|
|
|
|
uuid_multipath(const tree_ptr& t, uuid_multipath_node* h);
|
|
|
|
explicit uuid_multipath(const tree_ptr& tptr)
|
|
: uuid_multipath(tptr, tptr->root) {
|
|
// nop
|
|
}
|
|
|
|
uuid_multipath(uuid_multipath&& other) noexcept = default;
|
|
|
|
uuid_multipath(const uuid_multipath& other) = default;
|
|
|
|
uuid_multipath& operator=(uuid_multipath&& other) noexcept = default;
|
|
|
|
uuid_multipath& operator=(const uuid_multipath& other) = default;
|
|
|
|
auto id() const noexcept {
|
|
return head_->id_;
|
|
}
|
|
|
|
auto is_receiver() const noexcept {
|
|
return head_->is_receiver_;
|
|
}
|
|
|
|
bool equals(const uuid_multipath& other) const noexcept;
|
|
|
|
bool contains(uuid what) const noexcept;
|
|
|
|
size_t num_nodes() const noexcept {
|
|
return head_->down_.size();
|
|
}
|
|
|
|
template <class Inspector>
|
|
friend bool inspect(Inspector& f, uuid_multipath& x) {
|
|
if constexpr (Inspector::is_loading)
|
|
return x.head_->load(x.tree_->mem, f);
|
|
else
|
|
return x.head_->save(f);
|
|
}
|
|
|
|
private:
|
|
auto emplace(uuid id, bool is_receiver) {
|
|
return head_->down_.emplace(tree_->mem, id, is_receiver);
|
|
}
|
|
|
|
std::shared_ptr<uuid_multipath_tree> tree_;
|
|
|
|
uuid_multipath_node* head_;
|
|
};
|
|
|
|
/// @relates multipath
|
|
inline bool operator==(const uuid_multipath& x, const uuid_multipath& y) {
|
|
return x.equals(y);
|
|
}
|
|
|
|
/// @relates multipath
|
|
inline bool operator!=(const uuid_multipath& x, const uuid_multipath& y) {
|
|
return !(x == y);
|
|
}
|
|
|
|
// -- benchmark utilities ------------------------------------------------------
|
|
|
|
void run_streaming_benchmark();
|