-
Notifications
You must be signed in to change notification settings - Fork 6.7k
/
node.h
231 lines (193 loc) · 10.5 KB
/
node.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// 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_NODE_H_
#define IPCZ_SRC_IPCZ_NODE_H_
#include <functional>
#include "ipcz/api_object.h"
#include "ipcz/driver_memory.h"
#include "ipcz/ipcz.h"
#include "ipcz/link_side.h"
#include "ipcz/node_name.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
#include "third_party/abseil-cpp/absl/synchronization/mutex.h"
#include "third_party/abseil-cpp/absl/types/span.h"
namespace ipcz {
class NodeLink;
class NodeLinkMemory;
// A Node controls creation and interconnection of a collection of routers which
// can establish links to and from other routers in other nodes. Every node is
// assigned a globally unique name by a trusted broker node, and nodes may be
// introduced to each other exclusively through such brokers.
class Node : public APIObjectImpl<Node, APIObject::kNode> {
public:
enum class Type {
// A broker node assigns its own name and is able to assign names to other
// nodes upon connection. Brokers are trusted to introduce nodes to each
// other upon request, and brokers may connect to other brokers in order to
// share information and effectively bridge two node networks together.
kBroker,
// A "normal" (i.e. non-broker) node is assigned a permanent name by the
// first broker node it connects to, and it can only make contact with other
// nodes by requesting an introduction from that broker.
kNormal,
};
// Constructs a new node of the given `type`, using `driver` to support IPC.
// Note that `driver` must outlive the Node. `driver_node` is an arbitrary
// driver-specific handle that may be used for additional context when
// interfacing with the driver regarding this node.
Node(Type type, const IpczDriver& driver, IpczDriverHandle driver_node);
Type type() const { return type_; }
const IpczDriver& driver() const { return driver_; }
IpczDriverHandle driver_node() const { return driver_node_; }
// APIObject:
IpczResult Close() override;
// Connects to another node using `driver_transport` for I/O to and from the
// other node. `initial_portals` is a collection of new portals who may
// immediately begin to route parcels over a link to the new node, assuming
// the link is established successfully.
IpczResult ConnectNode(IpczDriverHandle driver_transport,
IpczConnectNodeFlags flags,
absl::Span<IpczHandle> initial_portals);
// Retrieves the name assigned to this node, if any.
NodeName GetAssignedName();
// Gets a reference to the node's broker link, if it has one.
Ref<NodeLink> GetBrokerLink();
// Sets this node's broker link, which is used e.g. to make introduction
// requests.
//
// This is called by a NodeConnector implementation after accepting a valid
// handshake message from a broker node, and `link` will be used as this
// node's permanent broker.
//
// Note that like any other NodeLink used by this Node, the same `link` must
// also be registered via AddLink() to associate it with its remote Node's
// name. This is also done by NodeConnector.
void SetBrokerLink(Ref<NodeLink> link);
// Sets this node's assigned name as given by a broker. NodeConnector is
// responsible for calling on non-broker Nodes this after receiving the
// expected handshake from a broker. Must not be called on broker nodes, as
// they assign their own name at construction time.
void SetAssignedName(const NodeName& name);
// Registers a new NodeLink for the given `remote_node_name`.
bool AddLink(const NodeName& remote_node_name, Ref<NodeLink> link);
// Returns a reference to the NodeLink used by this Node to communicate with
// the remote node identified by `name`; or null if this node has no NodeLink
// connected to that node.
Ref<NodeLink> GetLink(const NodeName& name);
// Generates a new random NodeName using this node's driver as a source of
// randomness.
NodeName GenerateRandomName() const;
// Sets a NodeLink to use for asynchronous shared memory allocation requests.
// This is configured when the ConnectNode() API is called with
// IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE. Typically this is combined with
// IPCZ_CONNECT_NODE_TO_BROKER when connecting from a sandboxed process which
// cannot allocate its own shared memory regions.
void SetAllocationDelegate(Ref<NodeLink> link);
// Requests allocation of a new shared memory object of the given size.
// `callback` is invoked with the new object when allocation is complete.
// This operation is asynchronous if allocation is delegated to another node,
// but if this node can allocate directly through the driver, `callback` is
// invoked with the result before this method returns.
using AllocateSharedMemoryCallback = std::function<void(DriverMemory)>;
void AllocateSharedMemory(size_t size, AllocateSharedMemoryCallback callback);
// Asynchronously attempts to establish a new NodeLink directly to the named
// node, invoking `callback` when complete. On success, this node will retain
// a new NodeLink to the named node, and `callback` will be invoked with a
// reference to that link. Otherwise `callback` will be invoked with a null
// reference.
//
// If the calling node already has a link to the named node, `callback` may
// be invoked synchronously with a link to that node before this method
// returns.
using EstablishLinkCallback = std::function<void(NodeLink*)>;
void EstablishLink(const NodeName& name, EstablishLinkCallback callback);
// Handles an incoming introduction request. Must only be called on a broker
// node. If this broker has a NodeLink to the node named by `for_node`, it
// will introduce that node and the remote node on `from_node_link`.
void HandleIntroductionRequest(NodeLink& from_node_link,
const NodeName& for_node);
// Accepts an introduction received from the broker. `transport` and `memory`
// can be used to establish a new NodeLink to the remote node, whose name is
// `name`. The NodeLink must assume a role as the given `side` of the link.
void AcceptIntroduction(NodeLink& from_node_link,
const NodeName& name,
LinkSide side,
uint32_t remote_protocol_version,
Ref<DriverTransport> transport,
Ref<NodeLinkMemory> memory);
// Handles a rejected introduction from the broker. This is called on a
// non-broker node that previously requested an introduction to `name` if
// the broker could not satisfy the request.
bool CancelIntroduction(const NodeName& name);
// Drops this node's link to the named node, if one exists.
void DropLink(const NodeName& name);
private:
~Node() override;
// Deactivates all NodeLinks and their underlying driver transports in
// preparation for this node's imminent destruction.
void ShutDown();
// Resolves all pending introduction requests with a null link, implying
// failure.
void CancelAllIntroductions();
const Type type_;
const IpczDriver& driver_;
const IpczDriverHandle driver_node_;
absl::Mutex mutex_;
// The name assigned to this node by the first broker it connected to, or
// self-assigned if this is a broker node. Once assigned, this name remains
// constant through the lifetime of the node.
NodeName assigned_name_ ABSL_GUARDED_BY(mutex_);
// A link to the first broker this node connected to. If this link is broken,
// the node will lose all its other links too.
Ref<NodeLink> broker_link_ ABSL_GUARDED_BY(mutex_);
// A link over which all internal shared memory allocation is delegated. If
// null, this Node will always attempt to allocate shared memory directly
// through its ipcz driver.
Ref<NodeLink> allocation_delegate_link_ ABSL_GUARDED_BY(mutex_);
// Lookup table of broker-assigned node names and links to those nodes. All of
// these links and their associated names are received by the `broker_link_`
// if this is a non-broker node. If this is a broker node, these links are
// either assigned by this node itself, or received from other brokers in the
// system.
using NodeLinkMap = absl::flat_hash_map<NodeName, Ref<NodeLink>>;
NodeLinkMap node_links_ ABSL_GUARDED_BY(mutex_);
// A map of other nodes to which this node is waiting for an introduction from
// `broker_link_`. Once such an introduction is received, all callbacks for
// that NodeName are executed.
using PendingIntroductionMap =
absl::flat_hash_map<NodeName, std::vector<EstablishLinkCallback>>;
PendingIntroductionMap pending_introductions_ ABSL_GUARDED_BY(mutex_);
// Nodes may race to request introductions to each other from the same broker.
// This can lead to redundant introductions being sent which the requesting
// nodes should be able to ignore. However, the following could occur on a
// broker which is processing a request from node A on Thread 1 while also
// processing a request from node B on thread 2:
//
// Thread 1 Thread 2 Time
// --- --- |
// A requests intro to B B requests intro to A v
// Send B intro X to A
// Send A intro Y to B
// Send A intro X to B
// Send B intro Y to A
//
// Each unique intro shares either end of a transport with its recipients,
// so both A and B must accept the same introduction (either X or Y). In this
// scenario however, A will first receive and accept intro X, and will ignore
// intro Y as redundant. But B will receive intro Y first and ignore intro X
// as redundant. This is bad.
//
// The set of `in_progress_introductions_` allows this (broker) node to guard
// against such interleaved introductions. Immediately before sending an intro
// to both recipients, a key identifying them is placed into the set. This key
// is removed immediately after both introductions are sent. If another thread
// is asked to introduce the same two nodes while the key is still present, it
// will ignore the request and send nothing.
using IntroductionKey = std::pair<NodeName, NodeName>;
absl::flat_hash_set<IntroductionKey> in_progress_introductions_
ABSL_GUARDED_BY(mutex_);
};
} // namespace ipcz
#endif // IPCZ_SRC_IPCZ_NODE_H_