blob: 54b903743e6692de9ac43a59d102f8fe660c87cc [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.
#ifndef IPCZ_SRC_IPCZ_ROUTER_H_
#define IPCZ_SRC_IPCZ_ROUTER_H_
#include <cstdint>
#include <utility>
#include "ipcz/ipcz.h"
#include "ipcz/parcel_queue.h"
#include "ipcz/route_edge.h"
#include "ipcz/router_descriptor.h"
#include "ipcz/router_link.h"
#include "ipcz/sequence_number.h"
#include "ipcz/trap_set.h"
#include "third_party/abseil-cpp/absl/synchronization/mutex.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "util/ref_counted.h"
namespace ipcz {
class NodeLink;
class RemoteRouterLink;
// The Router is the main primitive responsible for routing parcels between ipcz
// portals. This class is thread-safe.
//
// Before a Router can participate in any actual routing, it must have an
// outward link to another Router (see SetOutwardLink()). To establish a locally
// connected pair of Routers, pass both to LocalRouterLink::ConnectRouters(),
// which internally calls SetOutwardLink() on both:
//
// Router::Pair routers = {MakeRefCounted<Router>(),
// MakeRefCounted<Router>()};
// LocalRouterLink::ConnectRouters(LinkType::kCentral, routers);
//
// Each ipcz Portal directly controls a terminal Router along its route, and
// all routes stabilize to eventually consist of only two interconnected
// terminal Routers. When a portal moves, its side of the route is extended by
// creating a new terminal Router at the portal's new location. The previous
// terminal Router remains as a proxying hop to be phased out eventually.
class Router : public RefCounted {
public:
using Pair = std::pair<Ref<Router>, Ref<Router>>;
Router();
// Indicates whether the terminal router on the other side of the central link
// is known to be closed.
bool IsPeerClosed();
// Indicates whether the terminal router on the other side of the central link
// is known to be closed AND there are no more inbound parcels to be
// retrieved.
bool IsRouteDead();
// Fills in an IpczPortalStatus corresponding to the current state of this
// Router.
void QueryStatus(IpczPortalStatus& status);
// Returns true iff this Router's outward link is a LocalRouterLink between
// `this` and `other`.
bool HasLocalPeer(Router& router);
// Attempts to send an outbound parcel originating from this Router. Called
// only as a direct result of a Put() or EndPut() call on the router's owning
// portal.
IpczResult SendOutboundParcel(Parcel& parcel);
// Closes this side of the Router's own route. Only called on a Router to
// which a Portal is currently attached, and only by that Portal.
void CloseRoute();
// Uses `link` as this Router's new outward link. This is the primary link on
// which the router transmits parcels and control messages directed toward the
// other side of its route. Must only be called on a Router which has no
// outward link.
//
// NOTE: This is NOT safe to call when the other side of the link is already
// in active use by another Router, as `this` Router may already be in a
// transitional state and must be able to block decay around `link` from
// within this call.
void SetOutwardLink(Ref<RouterLink> link);
// Accepts an inbound parcel from the outward edge of this router, either to
// queue it for retrieval or forward it further inward.
bool AcceptInboundParcel(Parcel& parcel);
// Accepts an outbound parcel here from some other Router. The parcel is
// transmitted immediately or queued for later transmission over the Router's
// outward link. Called only on proxying Routers.
bool AcceptOutboundParcel(Parcel& parcel);
// Accepts notification that the other end of the route has been closed and
// that the closed end transmitted a total of `sequence_length` parcels before
// closing.
bool AcceptRouteClosureFrom(LinkType link_type,
SequenceNumber sequence_length);
// Accepts notification from a link bound to this Router that some node along
// the route (in the direction of that link) has been disconnected, e.g. due
// to a crash, and that the route is no longer functional as a result. This is
// similar to route closure, except no effort can realistically be made to
// deliver the complete sequence of parcels transmitted from that end of the
// route. `link_type` specifies the type of link which is propagating the
// notification to this rouer.
bool AcceptRouteDisconnectedFrom(LinkType link_type);
// Retrieves the next available inbound parcel from this Router, if present.
IpczResult GetNextInboundParcel(IpczGetFlags flags,
void* data,
size_t* num_bytes,
IpczHandle* handles,
size_t* num_handles);
// Attempts to install a new trap on this Router, to invoke `handler` as soon
// as one or more conditions in `conditions` is met. This method effectively
// implements the ipcz Trap() API. See its description in ipcz.h for details.
IpczResult Trap(const IpczTrapConditions& conditions,
IpczTrapEventHandler handler,
uint64_t context,
IpczTrapConditionFlags* satisfied_condition_flags,
IpczPortalStatus* status);
// Deserializes a new Router from `descriptor` received over `from_node_link`.
static Ref<Router> Deserialize(const RouterDescriptor& descriptor,
NodeLink& from_node_link);
// Serializes a description of a new Router which will be used to extend this
// Router's route across `to_node_link` by introducing a new Router on the
// remote node.
void SerializeNewRouter(NodeLink& to_node_link, RouterDescriptor& descriptor);
// Configures this Router to begin proxying incoming parcels toward (and
// outgoing parcels from) the Router described by `descriptor`, living on the
// remote node of `to_node_link`.
void BeginProxyingToNewRouter(NodeLink& to_node_link,
const RouterDescriptor& descriptor);
// Notifies this router that the given RemoteRouterLink has been disconnected
// due to an underlying NodeLink disconnection. This is only called for
// RemoteRouterLinks which are associated with this Router.
void NotifyLinkDisconnected(RemoteRouterLink& link);
// Flushes any inbound or outbound parcels, as well as any route closure
// notifications. RouterLinks which are no longer needed for the operation of
// this Router may be deactivated by this call.
//
// Since this may be called by many other Router methods, RouterLink
// implementations must exercise caution when calling into a Router to ensure
// that their own potentially reentrant deactivation by Flush() won't end up
// dropping the last reference and deleting `this` before Flush() returns.
//
// A safe way to ensure that is for RouterLink implementations to only call
// into Router using a reference held on the calling stack.
void Flush();
private:
~Router() override;
absl::Mutex mutex_;
// The current computed portal status to be reflected by a portal controlling
// this router, iff this is a terminal router.
IpczPortalStatus status_ ABSL_GUARDED_BY(mutex_) = {sizeof(status_)};
// A set of traps installed via a controlling portal where applicable. These
// traps are notified about any interesting state changes within the router.
TrapSet traps_ ABSL_GUARDED_BY(mutex_);
// The edge connecting this router outward to another, toward the portal on
// the other side of the route.
RouteEdge outward_edge_ ABSL_GUARDED_BY(mutex_);
// The edge connecting this router inward to another, closer to the portal on
// our own side of the route. Only present for proxying routers: terminal
// routers by definition can have no inward edge.
absl::optional<RouteEdge> inward_edge_ ABSL_GUARDED_BY(mutex_);
// Parcels received from the other end of the route. If this is a terminal
// router, these may be retrieved by the application via a controlling portal;
// otherwise they will be forwarded along `inward_edge_` as soon as possible.
ParcelQueue inbound_parcels_ ABSL_GUARDED_BY(mutex_);
// Parcels transmitted directly from this router (if sent by a controlling
// portal) or received from an inward peer which sent them outward toward this
// Router. These parcels generally only accumulate if there is no outward link
// present when attempting to transmit them, and they are forwarded along
// `outward_edge_` as soon as possible.
ParcelQueue outbound_parcels_ ABSL_GUARDED_BY(mutex_);
// Tracks whether this router has been unexpectedly disconnected from its
// links. This may be used to prevent additional links from being established.
bool is_disconnected_ ABSL_GUARDED_BY(mutex_) = false;
};
} // namespace ipcz
#endif // IPCZ_SRC_IPCZ_ROUTER_H_