rippled
Loading...
Searching...
No Matches
PathRequests.cpp
1#include <xrpld/app/ledger/LedgerMaster.h>
2#include <xrpld/app/main/Application.h>
3#include <xrpld/app/paths/PathRequests.h>
4
5#include <xrpl/basics/Log.h>
6#include <xrpl/core/JobQueue.h>
7#include <xrpl/protocol/ErrorCodes.h>
8#include <xrpl/protocol/RPCErr.h>
9#include <xrpl/protocol/jss.h>
10
11#include <algorithm>
12
13namespace xrpl {
14
20{
22
23 auto lineCache = lineCache_.lock();
24
25 std::uint32_t const lineSeq = lineCache ? lineCache->getLedger()->seq() : 0;
26 std::uint32_t const lgrSeq = ledger->seq();
27 JLOG(mJournal.debug()) << "getLineCache has cache for " << lineSeq << ", considering "
28 << lgrSeq;
29
30 if ((lineSeq == 0) || // no ledger
31 (authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger
32 (authoritative && ((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason
33 (lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason
34 {
35 JLOG(mJournal.debug()) << "getLineCache creating new cache for " << lgrSeq;
36 // Assign to the local before the member, because the member is a
37 // weak_ptr, and will immediately discard it if there are no other
38 // references.
39 lineCache_ = lineCache =
40 std::make_shared<RippleLineCache>(ledger, app_.journal("RippleLineCache"));
41 }
42 return lineCache;
43}
44
45void
47{
48 auto event = app_.getJobQueue().makeLoadEvent(jtPATH_FIND, "PathRequest::updateAll");
49
52
53 // Get the ledger and cache we should be using
54 {
56 requests = requests_;
57 cache = getLineCache(inLedger, true);
58 }
59
60 bool newRequests = app_.getLedgerMaster().isNewPathRequest();
61 bool mustBreak = false;
62
63 JLOG(mJournal.trace()) << "updateAll seq=" << cache->getLedger()->seq() << ", "
64 << requests.size() << " requests";
65
66 int processed = 0, removed = 0;
67
68 auto getSubscriber = [](PathRequest::pointer const& request) -> InfoSub::pointer {
69 if (auto ipSub = request->getSubscriber(); ipSub && ipSub->getRequest() == request)
70 {
71 return ipSub;
72 }
73 request->doAborting();
74 return nullptr;
75 };
76
77 do
78 {
79 JLOG(mJournal.trace()) << "updateAll looping";
80 for (auto const& wr : requests)
81 {
83 break;
84
85 auto request = wr.lock();
86 bool remove = true;
87 JLOG(mJournal.trace()) << "updateAll request " << (request ? "" : "not ") << "found";
88
89 if (request)
90 {
91 auto continueCallback = [&getSubscriber, &request]() {
92 // This callback is used by doUpdate to determine whether to
93 // continue working. If getSubscriber returns null, that
94 // indicates that this request is no longer relevant.
95 return (bool)getSubscriber(request);
96 };
97 if (!request->needsUpdate(newRequests, cache->getLedger()->seq()))
98 remove = false;
99 else
100 {
101 if (auto ipSub = getSubscriber(request))
102 {
103 if (!ipSub->getConsumer().warn())
104 {
105 // Release the shared ptr to the subscriber so that
106 // it can be freed if the client disconnects, and
107 // thus fail to lock later.
108 ipSub.reset();
109 Json::Value update = request->doUpdate(cache, false, continueCallback);
110 request->updateComplete();
111 update[jss::type] = "path_find";
112 if ((ipSub = getSubscriber(request)))
113 {
114 ipSub->send(update, false);
115 remove = false;
116 ++processed;
117 }
118 }
119 }
120 else if (request->hasCompletion())
121 {
122 // One-shot request with completion function
123 request->doUpdate(cache, false);
124 request->updateComplete();
125 ++processed;
126 }
127 }
128 }
129
130 if (remove)
131 {
133
134 // Remove any dangling weak pointers or weak
135 // pointers that refer to this path request.
136 auto ret = std::remove_if(
137 requests_.begin(), requests_.end(), [&removed, &request](auto const& wl) {
138 auto r = wl.lock();
139
140 if (r && r != request)
141 return false;
142 ++removed;
143 return true;
144 });
145
146 requests_.erase(ret, requests_.end());
147 }
148
149 mustBreak = !newRequests && app_.getLedgerMaster().isNewPathRequest();
150
151 // We weren't handling new requests and then
152 // there was a new request
153 if (mustBreak)
154 break;
155 }
156
157 if (mustBreak)
158 { // a new request came in while we were working
159 newRequests = true;
160 }
161 else if (newRequests)
162 { // we only did new requests, so we always need a last pass
163 newRequests = app_.getLedgerMaster().isNewPathRequest();
164 }
165 else
166 { // if there are no new requests, we are done
167 newRequests = app_.getLedgerMaster().isNewPathRequest();
168 if (!newRequests)
169 break;
170 }
171
172 // Hold on to the line cache until after the lock is released, so it can
173 // be destroyed outside of the lock
175 {
176 // Get the latest requests, cache, and ledger for next pass
178
179 if (requests_.empty())
180 break;
181 requests = requests_;
182 lastCache = cache;
183 cache = getLineCache(cache->getLedger(), false);
184 }
185 } while (!app_.getJobQueue().isStopping());
186
187 JLOG(mJournal.debug()) << "updateAll complete: " << processed << " processed and " << removed
188 << " removed";
189}
190
191bool
193{
195 return !requests_.empty();
196}
197
198void
200{
202
203 // Insert after any older unserviced requests but before
204 // any serviced requests
205 auto ret = std::find_if(requests_.begin(), requests_.end(), [](auto const& wl) {
206 auto r = wl.lock();
207
208 // We come before handled requests
209 return r && !r->isNew();
210 });
211
212 requests_.emplace(ret, req);
213}
214
215// Make a new-style path_find request
218 std::shared_ptr<InfoSub> const& subscriber,
219 std::shared_ptr<ReadView const> const& inLedger,
220 Json::Value const& requestJson)
221{
222 auto req = std::make_shared<PathRequest>(app_, subscriber, ++mLastIdentifier, *this, mJournal);
223
224 auto [valid, jvRes] = req->doCreate(getLineCache(inLedger, false), requestJson);
225
226 if (valid)
227 {
228 subscriber->setRequest(req);
231 }
232 return std::move(jvRes);
233}
234
235// Make an old-style ripple_path_find request
239 std::function<void(void)> completion,
240 Resource::Consumer& consumer,
241 std::shared_ptr<ReadView const> const& inLedger,
242 Json::Value const& request)
243{
244 // This assignment must take place before the
245 // completion function is called
247 app_, completion, consumer, ++mLastIdentifier, *this, mJournal);
248
249 auto [valid, jvRes] = req->doCreate(getLineCache(inLedger, false), request);
250
251 if (!valid)
252 {
253 req.reset();
254 }
255 else
256 {
259 {
260 // The newPathRequest failed. Tell the caller.
261 jvRes = rpcError(rpcTOO_BUSY);
262 req.reset();
263 }
264 }
265
266 return std::move(jvRes);
267}
268
271 Resource::Consumer& consumer,
272 std::shared_ptr<ReadView const> const& inLedger,
273 Json::Value const& request)
274{
275 auto cache = std::make_shared<RippleLineCache>(inLedger, app_.journal("RippleLineCache"));
276
277 auto req =
278 std::make_shared<PathRequest>(app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal);
279
280 auto [valid, jvRes] = req->doCreate(cache, request);
281 if (valid)
282 jvRes = req->doUpdate(cache, false);
283 return std::move(jvRes);
284}
285
286} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
bool isStopping() const
Definition JobQueue.h:209
std::unique_ptr< LoadEvent > makeLoadEvent(JobType t, std::string const &name)
Return a scoped LoadEvent.
Definition JobQueue.cpp:145
beast::Journal mJournal
Application & app_
std::weak_ptr< RippleLineCache > lineCache_
bool requestsPending() const
std::shared_ptr< RippleLineCache > getLineCache(std::shared_ptr< ReadView const > const &ledger, bool authoritative)
Get the current RippleLineCache, updating it if necessary.
Json::Value makePathRequest(std::shared_ptr< InfoSub > const &subscriber, std::shared_ptr< ReadView const > const &ledger, Json::Value const &request)
Json::Value makeLegacyPathRequest(PathRequest::pointer &req, std::function< void(void)> completion, Resource::Consumer &consumer, std::shared_ptr< ReadView const > const &inLedger, Json::Value const &request)
void insertPathRequest(PathRequest::pointer const &)
Json::Value doLegacyPathRequest(Resource::Consumer &consumer, std::shared_ptr< ReadView const > const &inLedger, Json::Value const &request)
void updateAll(std::shared_ptr< ReadView const > const &ledger)
Update all of the contained PathRequest instances.
std::recursive_mutex mLock
std::atomic< int > mLastIdentifier
std::vector< PathRequest::wptr > requests_
An endpoint that consumes resources.
Definition Consumer.h:16
virtual JobQueue & getJobQueue()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual beast::Journal journal(std::string const &name)=0
T find_if(T... args)
T is_same_v
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ jtPATH_FIND
Definition Job.h:63
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
@ rpcTOO_BUSY
Definition ErrorCodes.h:36
T remove_if(T... args)
T reset(T... args)
T size(T... args)