forked from XRPLF/rippled
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathBasicNetwork.h
259 lines (209 loc) · 7.75 KB
/
BasicNetwork.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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_CSF_BASICNETWORK_H_INCLUDED
#define RIPPLE_TEST_CSF_BASICNETWORK_H_INCLUDED
#include <test/csf/Scheduler.h>
#include <test/csf/Digraph.h>
namespace ripple {
namespace test {
namespace csf {
/** Peer to peer network simulator.
The network is formed from a set of Peer objects representing
vertices and configurable connections representing edges.
The caller is responsible for creating the Peer objects ahead
of time.
Peer objects cannot be destroyed once the BasicNetwork is
constructed. To handle peers going online and offline,
callers can simply disconnect all links and reconnect them
later. Connections are directed, one end is the inbound
Peer and the other is the outbound Peer.
Peers may send messages along their connections. To simulate
the effects of latency, these messages can be delayed by a
configurable duration set when the link is established.
Messages always arrive in the order they were sent on a
particular connection.
A message is modeled using a lambda function. The caller
provides the code to execute upon delivery of the message.
If a Peer is disconnected, all messages pending delivery
at either end of the connection will not be delivered.
When creating the Peer set, the caller needs to provide a
Scheduler object for managing the the timing and delivery
of messages. After constructing the network, and establishing
connections, the caller uses the scheduler's step_* functions
to drive messages through the network.
The graph of peers and connections is internally represented
using Digraph<Peer,BasicNetwork::link_type>. Clients have
const access to that graph to perform additional operations not
directly provided by BasicNetwork.
Peer Requirements:
Peer should be a lightweight type, cheap to copy
and/or move. A good candidate is a simple pointer to
the underlying user defined type in the simulation.
Expression Type Requirements
---------- ---- ------------
P Peer
u, v Values of type P
P u(v) CopyConstructible
u.~P() Destructible
u == v bool EqualityComparable
u < v bool LessThanComparable
std::hash<P> class std::hash is defined for P
! u bool true if u is not-a-peer
*/
template <class Peer>
class BasicNetwork
{
using peer_type = Peer;
using clock_type = Scheduler::clock_type;
using duration = typename clock_type::duration;
using time_point = typename clock_type::time_point;
struct link_type
{
bool inbound = false;
duration delay{};
time_point established{};
link_type() = default;
link_type(bool inbound_, duration delay_, time_point established_)
: inbound(inbound_), delay(delay_), established(established_)
{
}
};
Scheduler& scheduler;
Digraph<Peer, link_type> links_;
public:
BasicNetwork(BasicNetwork const&) = delete;
BasicNetwork&
operator=(BasicNetwork const&) = delete;
BasicNetwork(Scheduler& s);
/** Connect two peers.
The link is directed, with `from` establishing
the outbound connection and `to` receiving the
incoming connection.
Preconditions:
from != to (self connect disallowed).
A link between from and to does not
already exist (duplicates disallowed).
Effects:
Creates a link between from and to.
@param `from` The source of the outgoing connection
@param `to` The recipient of the incoming connection
@param `delay` The time delay of all delivered messages
@return `true` if a new connection was established
*/
bool
connect(
Peer const& from,
Peer const& to,
duration const& delay = std::chrono::seconds{0});
/** Break a link.
Effects:
If a connection is present, both ends are
disconnected.
Any pending messages on the connection
are discarded.
@return `true` if a connection was broken.
*/
bool
disconnect(Peer const& peer1, Peer const& peer2);
/** Send a message to a peer.
Preconditions:
A link exists between from and to.
Effects:
If the link is not broken when the
link's `delay` time has elapsed,
the function will be invoked with
no arguments.
@note Its the caller's responsibility to
ensure that the body of the function performs
activity consistent with `from`'s receipt of
a message from `to`.
*/
template <class Function>
void
send(Peer const& from, Peer const& to, Function&& f);
/** Return the range of active links.
@return A random access range over Digraph::Edge instances
*/
auto
links(Peer const& from)
{
return links_.outEdges(from);
}
/** Return the underlying digraph
*/
Digraph<Peer, link_type> const &
graph() const
{
return links_;
}
};
//------------------------------------------------------------------------------
template <class Peer>
BasicNetwork<Peer>::BasicNetwork(Scheduler& s) : scheduler(s)
{
}
template <class Peer>
inline bool
BasicNetwork<Peer>::connect(
Peer const& from,
Peer const& to,
duration const& delay)
{
if (to == from)
return false;
time_point const now = scheduler.now();
if(!links_.connect(from, to, link_type{false, delay, now}))
return false;
auto const result = links_.connect(to, from, link_type{true, delay, now});
(void)result;
assert(result);
return true;
}
template <class Peer>
inline bool
BasicNetwork<Peer>::disconnect(Peer const& peer1, Peer const& peer2)
{
if (! links_.disconnect(peer1, peer2))
return false;
bool r = links_.disconnect(peer2, peer1);
(void)r;
assert(r);
return true;
}
template <class Peer>
template <class Function>
inline void
BasicNetwork<Peer>::send(Peer const& from, Peer const& to, Function&& f)
{
auto link = links_.edge(from,to);
if(!link)
return;
time_point const sent = scheduler.now();
scheduler.in(
link->delay,
[ from, to, sent, f = std::forward<Function>(f), this ] {
// only process if still connected and connection was
// not broken since the message was sent
auto link = links_.edge(from, to);
if (link && link->established <= sent)
f();
});
}
} // namespace csf
} // namespace test
} // namespace ripple
#endif