blob: d2b831b92092986e035e6afcac4ab29c571c2b9f [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <condition_variable>
#include <mutex>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <vector>
#include "bandwidth.h"
#include "internal_list.h"
#include "node_id.h"
#include "optional.h"
#include "timer.h"
#include <iostream>
namespace overnet {
namespace routing_table_impl {
struct FullLinkLabel {
NodeId from;
NodeId to;
uint64_t link_label;
};
inline bool operator==(const FullLinkLabel& a, const FullLinkLabel& b) {
return a.from == b.from && a.to == b.to && a.link_label == b.link_label;
}
} // namespace routing_table_impl
} // namespace overnet
namespace std {
template <>
struct hash<overnet::routing_table_impl::FullLinkLabel> {
size_t operator()(
const overnet::routing_table_impl::FullLinkLabel& id) const {
return id.from.Hash() ^ id.to.Hash() ^ id.link_label;
}
};
} // namespace std
namespace overnet {
enum class LinkMedia : uint8_t {
Unknown,
Wired,
Wireless,
Internet,
};
static constexpr uint64_t METRIC_VERSION_TOMBSTONE = ~uint64_t(0);
class LinkMetrics {
public:
LinkMetrics(NodeId from, NodeId to, uint64_t version, uint64_t link_label)
: from_(from), to_(to), version_(version), link_label_(link_label) {}
NodeId from() const { return from_; }
NodeId to() const { return to_; }
uint64_t version() const { return version_; }
uint64_t link_label() const { return link_label_; }
Bandwidth bw_link() const { return bw_link_; }
Bandwidth bw_used() const { return bw_used_; }
TimeDelta rtt() const { return rtt_; }
LinkMedia media() const { return media_; }
void set_bw_link(Bandwidth x) { bw_link_ = x; }
void set_bw_used(Bandwidth x) { bw_used_ = x; }
void set_rtt(TimeDelta x) { rtt_ = x; }
void set_media(LinkMedia x) { media_ = x; }
private:
NodeId from_;
NodeId to_;
uint64_t version_;
uint64_t link_label_;
Bandwidth bw_link_{Bandwidth::Zero()};
Bandwidth bw_used_{Bandwidth::Zero()};
TimeDelta rtt_{TimeDelta::PositiveInf()};
LinkMedia media_{LinkMedia::Unknown};
};
std::ostream& operator<<(std::ostream& out, const LinkMetrics& m);
class NodeMetrics {
public:
NodeMetrics(NodeId node_id, uint64_t version)
: node_id_(node_id), version_(version) {}
NodeId node_id() const { return node_id_; }
uint64_t version() const { return version_; }
uint8_t battery_pct() const { return battery_pct_; }
TimeDelta forwarding_time() const { return forwarding_time_; }
void set_battery_pct(uint8_t battery_pct) { battery_pct_ = battery_pct; }
void set_forwarding_time(TimeDelta forwarding_time) {
forwarding_time_ = forwarding_time;
}
private:
NodeId node_id_;
uint64_t version_;
uint8_t battery_pct_ = 100;
TimeDelta forwarding_time_{TimeDelta::PositiveInf()};
};
std::ostream& operator<<(std::ostream& out, const LinkMetrics& m);
class RoutingTable {
public:
RoutingTable(NodeId root_node, Timer* timer, bool allow_threading)
: root_node_(root_node),
timer_(timer),
allow_threading_(allow_threading) {}
~RoutingTable();
static constexpr TimeDelta EntryExpiry() { return TimeDelta::FromMinutes(5); }
using SelectedLinks = std::unordered_map<NodeId, uint64_t>;
void Update(std::vector<NodeMetrics> node_metrics,
std::vector<LinkMetrics> link_metrics, bool flush_old_nodes);
// Returns true if this update concludes any changes begun by all prior
// Update() calls.
template <class F>
bool PollLinkUpdates(F f) {
if (!mu_.try_lock()) {
return false;
}
if (selected_links_version_ != published_links_version_) {
published_links_version_ = selected_links_version_;
f(selected_links_);
}
const bool done = !processing_changes_.has_value();
mu_.unlock();
return done;
}
void BlockUntilNoBackgroundUpdatesProcessing() {
std::unique_lock<std::mutex> lock(mu_);
cv_.wait(lock, [this]() -> bool { return !processing_changes_; });
}
private:
const NodeId root_node_;
Timer* const timer_;
struct Metrics {
std::vector<NodeMetrics> node_metrics;
std::vector<LinkMetrics> link_metrics;
bool Empty() const { return node_metrics.empty() && link_metrics.empty(); }
void Clear() {
node_metrics.clear();
link_metrics.clear();
}
};
Metrics change_log_;
const bool allow_threading_;
bool flush_requested_ = false;
void ApplyChanges(TimeStamp now, const Metrics& changes, bool flush);
SelectedLinks BuildForwardingTable();
TimeStamp last_update_{TimeStamp::Epoch()};
uint64_t path_finding_run_ = 0;
std::mutex mu_;
std::condition_variable cv_;
Optional<std::thread> processing_changes_;
struct Node;
struct Link {
Link(TimeStamp now, LinkMetrics initial_metrics, Node* to)
: metrics(initial_metrics), last_updated(now), to_node(to) {}
LinkMetrics metrics;
TimeStamp last_updated;
InternalListNode<Link> outgoing_link;
Node* const to_node;
};
struct Node {
Node(TimeStamp now, NodeMetrics initial_metrics)
: metrics(initial_metrics), last_updated(now) {}
NodeMetrics metrics;
TimeStamp last_updated;
InternalList<Link, &Link::outgoing_link> outgoing_links;
// Path finding temporary state.
uint64_t last_path_finding_run = 0;
TimeDelta best_rtt{TimeDelta::Zero()};
Node* best_from;
Link* best_link;
bool queued = false;
InternalListNode<Node> path_finding_node;
};
void RemoveOutgoingLinks(Node& node);
std::unordered_map<NodeId, Node> node_metrics_;
std::unordered_map<routing_table_impl::FullLinkLabel, Link> link_metrics_;
uint64_t selected_links_version_ = 0;
SelectedLinks selected_links_;
uint64_t published_links_version_ = 0;
};
} // namespace overnet