Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

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();