blob: 06e3c0055a17f8e4a2aeea8b80aaf4595fee0131 [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include "ipcz/driver_memory.h"
#include "ipcz/driver_transport.h"
#include "ipcz/link_side.h"
#include "ipcz/link_type.h"
#include "ipcz/node.h"
#include "ipcz/node_link_memory.h"
#include "ipcz/node_messages.h"
#include "ipcz/node_name.h"
#include "ipcz/sequence_number.h"
#include "ipcz/sublink_id.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/abseil-cpp/absl/synchronization/mutex.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/span.h"
#include "util/ref_counted.h"
namespace ipcz {
class Message;
class RemoteRouterLink;
class Router;
// A NodeLink instance encapsulates all communication from its owning node to
// exactly one other remote node in the sytem. Each NodeLink manages a
// DriverTransport for general-purpose I/O to and from the remote node.
// NodeLinks may also allocate an arbitrary number of sublinks which are used
// to multiplex the link and facilitate point-to-point communication between
// specific Router instances on either end.
class NodeLink : public msg::NodeMessageListener {
struct Sublink {
Sublink(Ref<RemoteRouterLink> link, Ref<Router> receiver);
Sublink(const Sublink&);
Sublink& operator=(Sublink&&);
Sublink& operator=(const Sublink&);
Ref<RemoteRouterLink> router_link;
Ref<Router> receiver;
static Ref<NodeLink> Create(Ref<Node> node,
LinkSide link_side,
const NodeName& local_node_name,
const NodeName& remote_node_name,
Node::Type remote_node_type,
uint32_t remote_protocol_version,
Ref<DriverTransport> transport,
Ref<NodeLinkMemory> memory);
const Ref<Node>& node() const { return node_; }
LinkSide link_side() const { return link_side_; }
const NodeName& local_node_name() const { return local_node_name_; }
const NodeName& remote_node_name() const { return remote_node_name_; }
Node::Type remote_node_type() const { return remote_node_type_; }
uint32_t remote_protocol_version() const { return remote_protocol_version_; }
const Ref<DriverTransport>& transport() const { return transport_; }
NodeLinkMemory& memory() { return *memory_; }
const NodeLinkMemory& memory() const { return *memory_; }
// Binds `sublink` on this NodeLink to the given `router`. `link_side`
// specifies which side of the link this end identifies as (A or B), and
// `type` specifies the type of link this is, from the perspective of
// `router`.
// If `link_state_fragment` is non-null, the given fragment contains the
// shared RouterLinkState structure for the new link. Only central links
// require a RouterLinkState.
Ref<RemoteRouterLink> AddRemoteRouterLink(
SublinkId sublink,
FragmentRef<RouterLinkState> link_state,
LinkType type,
LinkSide side,
Ref<Router> router);
// Removes the route specified by `sublink`. Once removed, any messages
// received for that sublink are ignored.
void RemoveRemoteRouterLink(SublinkId sublink);
// Retrieves the Router and RemoteRouterLink currently bound to `sublink`
// on this NodeLink.
absl::optional<Sublink> GetSublink(SublinkId sublink);
// Retrieves only the Router currently bound to `sublink` on this NodeLink.
Ref<Router> GetRouter(SublinkId sublink);
// Sends a new driver memory object to the remote endpoint to be associated
// with BufferId within the peer NodeLink's associated NodeLinkMemory, and to
// be used to dynamically allocate blocks of `block_size` bytes. The BufferId
// must have already been reserved locally by this NodeLink using
// AllocateNewBufferId().
void AddBlockBuffer(BufferId id, uint32_t block_size, DriverMemory memory);
// Asks the broker on the other end of this link to introduce the local node
// to the node identified by `name`. This will always elicit a response from
// the broker in the form of either an AcceptIntroduction or
// RejectIntroduction message.
void RequestIntroduction(const NodeName& name);
// Introduces the remote node to the node named `name`, with details needed to
// construct a new NodeLink to that node.
void AcceptIntroduction(const NodeName& name,
LinkSide side,
uint32_t remote_protocol_version,
Ref<DriverTransport> transport,
DriverMemory memory);
// Rejects an introduction request previously sent by the remote node for the
// node identified by `name`.
void RejectIntroduction(const NodeName& name);
// Permanently deactivates this NodeLink. Once this call returns the NodeLink
// will no longer receive transport messages. It may still be used to transmit
// outgoing messages, but it cannot be reactivated. Transmissions over a
// deactivated transport may or may not guarantee delivery to the peer
// transport, as this is left to driver's discretion.
void Deactivate();
// Finalizes serialization of DriverObjects within `message` and transmits it
// to the NodeLink's peer, either over the DriverTransport or through shared
// memory.
void Transmit(Message& message);
NodeLink(Ref<Node> node,
LinkSide link_side,
const NodeName& local_node_name,
const NodeName& remote_node_name,
Node::Type remote_node_type,
uint32_t remote_protocol_version,
Ref<DriverTransport> transport,
Ref<NodeLinkMemory> memory);
~NodeLink() override;
SequenceNumber GenerateOutgoingSequenceNumber();
// NodeMessageListener overrides:
bool OnRequestIntroduction(msg::RequestIntroduction& request) override;
bool OnAcceptIntroduction(msg::AcceptIntroduction& accept) override;
bool OnRejectIntroduction(msg::RejectIntroduction& reject) override;
bool OnAddBlockBuffer(msg::AddBlockBuffer& add) override;
bool OnAcceptParcel(msg::AcceptParcel& accept) override;
bool OnRouteClosed(msg::RouteClosed& route_closed) override;
bool OnSetRouterLinkState(msg::SetRouterLinkState& set) override;
bool OnFlushRouter(msg::FlushRouter& flush) override;
void OnTransportError() override;
const Ref<Node> node_;
const LinkSide link_side_;
const NodeName local_node_name_;
const NodeName remote_node_name_;
const Node::Type remote_node_type_;
const uint32_t remote_protocol_version_;
const Ref<DriverTransport> transport_;
const Ref<NodeLinkMemory> memory_;
absl::Mutex mutex_;
bool active_ ABSL_GUARDED_BY(mutex_) = true;
// Messages transmitted from this NodeLink may traverse either the driver
// transport OR a shared memory queue. Messages transmitted from the same
// thread need to be processed in the same order by the receiving node. This
// is used to generate a sequence number for every message so they can be
// reordered on the receiving end.
std::atomic<uint64_t> next_outgoing_sequence_number_generator_{0};
using SublinkMap = absl::flat_hash_map<SublinkId, Sublink>;
SublinkMap sublinks_ ABSL_GUARDED_BY(mutex_);
} // namespace ipcz