xrpld
Loading...
Searching...
No Matches
PathRequestManager.cpp
1#include <xrpld/rpc/detail/PathRequestManager.h>
2
3#include <xrpld/app/ledger/LedgerMaster.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/rpc/detail/AssetCache.h>
6#include <xrpld/rpc/detail/PathRequest.h>
7
8#include <xrpl/basics/Log.h>
9#include <xrpl/core/Job.h>
10#include <xrpl/core/JobQueue.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/ledger/ReadView.h>
13#include <xrpl/protocol/ErrorCodes.h>
14#include <xrpl/protocol/RPCErr.h>
15#include <xrpl/protocol/jss.h>
16#include <xrpl/resource/Consumer.h>
17#include <xrpl/server/InfoSub.h>
18
19#include <algorithm>
20#include <cstdint>
21#include <functional>
22#include <memory>
23#include <mutex>
24#include <utility>
25#include <vector>
26
27namespace xrpl {
28
32std::shared_ptr<AssetCache>
34{
35 std::scoped_lock const sl(lock_);
36
37 auto assetCache = assetCache_.lock();
38
39 std::uint32_t const lineSeq = assetCache ? assetCache->getLedger()->seq() : 0;
40 std::uint32_t const lgrSeq = ledger->seq();
41 JLOG(journal_.debug()) << "getLineCache has cache for " << lineSeq << ", considering "
42 << lgrSeq;
43
44 if ((lineSeq == 0) || // no ledger
45 (authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger
46 (authoritative && ((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason
47 (lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason
48 {
49 JLOG(journal_.debug()) << "getLineCache creating new cache for " << lgrSeq;
50 // Assign to the local before the member, because the member is a
51 // weak_ptr, and will immediately discard it if there are no other
52 // references.
53 assetCache_ = assetCache =
54 std::make_shared<AssetCache>(ledger, app_.getJournal("AssetCache"));
55 }
56 return assetCache;
57}
58
59void
61{
62 auto event = app_.getJobQueue().makeLoadEvent(JtPathFind, "PathRequest::updateAll");
63
66
67 // Get the ledger and cache we should be using
68 {
69 std::scoped_lock const sl(lock_);
70 requests = requests_;
71 cache = getAssetCache(inLedger, true);
72 }
73
74 bool newRequests = app_.getLedgerMaster().isNewPathRequest();
75 bool mustBreak = false;
76
77 JLOG(journal_.trace()) << "updateAll seq=" << cache->getLedger()->seq() << ", "
78 << requests.size() << " requests";
79
80 int processed = 0, removed = 0;
81
82 auto getSubscriber = [](PathRequest::pointer const& request) -> InfoSub::pointer {
83 if (auto ipSub = request->getSubscriber(); ipSub && ipSub->getRequest() == request)
84 {
85 return ipSub;
86 }
87 request->doAborting();
88 return nullptr;
89 };
90
91 do
92 {
93 JLOG(journal_.trace()) << "updateAll looping";
94 for (auto const& wr : requests)
95 {
96 if (app_.getJobQueue().isStopping())
97 break;
98
99 auto request = wr.lock();
100 bool remove = true;
101 JLOG(journal_.trace()) << "updateAll request " << (request ? "" : "not ") << "found";
102
103 if (request)
104 {
105 auto continueCallback = [&getSubscriber, &request]() {
106 // This callback is used by doUpdate to determine whether to
107 // continue working. If getSubscriber returns null, that
108 // indicates that this request is no longer relevant.
109 return (bool)getSubscriber(request);
110 };
111 if (!request->needsUpdate(newRequests, cache->getLedger()->seq()))
112 {
113 remove = false;
114 }
115 else
116 {
117 if (auto ipSub = getSubscriber(request))
118 {
119 if (!ipSub->getConsumer().warn())
120 {
121 // Release the shared ptr to the subscriber so that
122 // it can be freed if the client disconnects, and
123 // thus fail to lock later.
124 ipSub.reset();
125 json::Value update = request->doUpdate(cache, false, continueCallback);
126 request->updateComplete();
127 update[jss::type] = "path_find";
128 ipSub = getSubscriber(request);
129 if (ipSub)
130 {
131 ipSub->send(update, false);
132 remove = false;
133 ++processed;
134 }
135 }
136 }
137 else if (request->hasCompletion())
138 {
139 // One-shot request with completion function
140 request->doUpdate(cache, false);
141 request->updateComplete();
142 ++processed;
143 }
144 }
145 }
146
147 if (remove)
148 {
149 std::scoped_lock const sl(lock_);
150
151 // Remove any dangling weak pointers or weak
152 // pointers that refer to this path request.
153 auto ret = std::ranges::remove_if(requests_, [&removed, &request](auto const& wl) {
154 auto r = wl.lock();
155
156 if (r && r != request)
157 return false;
158 ++removed;
159 return true;
160 });
161
162 requests_.erase(ret.begin(), ret.end());
163 }
164
165 mustBreak = !newRequests && app_.getLedgerMaster().isNewPathRequest();
166
167 // We weren't handling new requests and then
168 // there was a new request
169 if (mustBreak)
170 break;
171 }
172
173 if (mustBreak)
174 { // a new request came in while we were working
175 newRequests = true;
176 }
177 else if (newRequests)
178 { // we only did new requests, so we always need a last pass
179 newRequests = app_.getLedgerMaster().isNewPathRequest();
180 }
181 else
182 { // if there are no new requests, we are done
183 newRequests = app_.getLedgerMaster().isNewPathRequest();
184 if (!newRequests)
185 break;
186 }
187
188 // Hold on to the line cache until after the lock is released, so it can
189 // be destroyed outside of the lock
191 {
192 // Get the latest requests, cache, and ledger for next pass
193 std::scoped_lock const sl(lock_);
194
195 if (requests_.empty())
196 break;
197 requests = requests_;
198 lastCache = cache;
199 cache = getAssetCache(cache->getLedger(), false);
200 }
201 } while (!app_.getJobQueue().isStopping());
202
203 JLOG(journal_.debug()) << "updateAll complete: " << processed << " processed and " << removed
204 << " removed";
205}
206
207bool
209{
210 std::scoped_lock const sl(lock_);
211 return !requests_.empty();
212}
213
214void
216{
217 std::scoped_lock const sl(lock_);
218
219 // Insert after any older unserviced requests but before
220 // any serviced requests
221 auto ret = std::ranges::find_if(requests_, [](auto const& wl) {
222 auto r = wl.lock();
223
224 // We come before handled requests
225 return r && !r->isNew();
226 });
227
228 requests_.emplace(ret, req);
229}
230
231// Make a new-style path_find request
234 std::shared_ptr<InfoSub> const& subscriber,
235 std::shared_ptr<ReadView const> const& inLedger,
236 json::Value const& requestJson)
237{
238 auto req = std::make_shared<PathRequest>(app_, subscriber, ++lastIdentifier_, *this, journal_);
239
240 auto [valid, jvRes] = req->doCreate(getAssetCache(inLedger, false), requestJson);
241
242 if (valid)
243 {
244 subscriber->setRequest(req);
246 app_.getLedgerMaster().newPathRequest();
247 }
248 return std::move(jvRes);
249}
250
251// Make an old-style ripple_path_find request
255 std::function<void(void)> completion,
256 Resource::Consumer& consumer,
257 std::shared_ptr<ReadView const> const& inLedger,
258 json::Value const& request)
259{
260 // This assignment must take place before the
261 // completion function is called
263 app_, completion, consumer, ++lastIdentifier_, *this, journal_);
264
265 auto [valid, jvRes] = req->doCreate(getAssetCache(inLedger, false), request);
266
267 if (!valid)
268 {
269 req.reset();
270 }
271 else
272 {
274 if (!app_.getLedgerMaster().newPathRequest())
275 {
276 // The newPathRequest failed. Tell the caller.
277 jvRes = rpcError(RpcTooBusy);
278 req.reset();
279 }
280 }
281
282 return std::move(jvRes);
283}
284
287 Resource::Consumer& consumer,
288 std::shared_ptr<ReadView const> const& inLedger,
289 json::Value const& request)
290{
291 auto cache = std::make_shared<AssetCache>(inLedger, app_.getJournal("AssetCache"));
292
293 auto req =
294 std::make_shared<PathRequest>(app_, [] {}, consumer, ++lastIdentifier_, *this, journal_);
295
296 auto [valid, jvRes] = req->doCreate(cache, request);
297 if (valid)
298 jvRes = req->doUpdate(cache, false);
299 return std::move(jvRes);
300}
301
302} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
std::shared_ptr< InfoSub > pointer
Definition InfoSub.h:47
std::atomic< int > lastIdentifier_
json::Value doLegacyPathRequest(Resource::Consumer &consumer, std::shared_ptr< ReadView const > const &inLedger, json::Value const &request)
std::vector< PathRequest::wptr > requests_
std::shared_ptr< AssetCache > getAssetCache(std::shared_ptr< ReadView const > const &ledger, bool authoritative)
Get the current AssetCache, updating it if necessary.
std::weak_ptr< AssetCache > assetCache_
json::Value makePathRequest(std::shared_ptr< InfoSub > const &subscriber, std::shared_ptr< ReadView const > const &ledger, json::Value const &request)
void updateAll(std::shared_ptr< ReadView const > const &ledger)
Update all of the contained PathRequest instances.
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 &)
std::recursive_mutex lock_
std::shared_ptr< PathRequest > pointer
Definition PathRequest.h:36
An endpoint that consumes resources.
Definition Consumer.h:15
T find_if(T... args)
T make_shared(T... args)
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
@ RpcTooBusy
Definition ErrorCodes.h:38
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
@ JtPathFind
Definition Job.h:65
T remove_if(T... args)
T reset(T... args)
T size(T... args)