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 " << lgrSeq;
28
29 if ((lineSeq == 0) || // no ledger
30 (authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger
31 (authoritative && ((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason
32 (lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason
33 {
34 JLOG(mJournal.debug()) << "getLineCache creating new cache for " << lgrSeq;
35 // Assign to the local before the member, because the member is a
36 // weak_ptr, and will immediately discard it if there are no other
37 // references.
38 lineCache_ = lineCache = std::make_shared<RippleLineCache>(ledger, app_.journal("RippleLineCache"));
39 }
40 return lineCache;
41}
42
43void
45{
46 auto event = app_.getJobQueue().makeLoadEvent(jtPATH_FIND, "PathRequest::updateAll");
47
50
51 // Get the ledger and cache we should be using
52 {
54 requests = requests_;
55 cache = getLineCache(inLedger, true);
56 }
57
58 bool newRequests = app_.getLedgerMaster().isNewPathRequest();
59 bool mustBreak = false;
60
61 JLOG(mJournal.trace()) << "updateAll seq=" << cache->getLedger()->seq() << ", " << requests.size() << " requests";
62
63 int processed = 0, removed = 0;
64
65 auto getSubscriber = [](PathRequest::pointer const& request) -> InfoSub::pointer {
66 if (auto ipSub = request->getSubscriber(); ipSub && ipSub->getRequest() == request)
67 {
68 return ipSub;
69 }
70 request->doAborting();
71 return nullptr;
72 };
73
74 do
75 {
76 JLOG(mJournal.trace()) << "updateAll looping";
77 for (auto const& wr : requests)
78 {
80 break;
81
82 auto request = wr.lock();
83 bool remove = true;
84 JLOG(mJournal.trace()) << "updateAll request " << (request ? "" : "not ") << "found";
85
86 if (request)
87 {
88 auto continueCallback = [&getSubscriber, &request]() {
89 // This callback is used by doUpdate to determine whether to
90 // continue working. If getSubscriber returns null, that
91 // indicates that this request is no longer relevant.
92 return (bool)getSubscriber(request);
93 };
94 if (!request->needsUpdate(newRequests, cache->getLedger()->seq()))
95 remove = false;
96 else
97 {
98 if (auto ipSub = getSubscriber(request))
99 {
100 if (!ipSub->getConsumer().warn())
101 {
102 // Release the shared ptr to the subscriber so that
103 // it can be freed if the client disconnects, and
104 // thus fail to lock later.
105 ipSub.reset();
106 Json::Value update = request->doUpdate(cache, false, continueCallback);
107 request->updateComplete();
108 update[jss::type] = "path_find";
109 if ((ipSub = getSubscriber(request)))
110 {
111 ipSub->send(update, false);
112 remove = false;
113 ++processed;
114 }
115 }
116 }
117 else if (request->hasCompletion())
118 {
119 // One-shot request with completion function
120 request->doUpdate(cache, false);
121 request->updateComplete();
122 ++processed;
123 }
124 }
125 }
126
127 if (remove)
128 {
130
131 // Remove any dangling weak pointers or weak
132 // pointers that refer to this path request.
133 auto ret = std::remove_if(requests_.begin(), requests_.end(), [&removed, &request](auto const& wl) {
134 auto r = wl.lock();
135
136 if (r && r != request)
137 return false;
138 ++removed;
139 return true;
140 });
141
142 requests_.erase(ret, requests_.end());
143 }
144
145 mustBreak = !newRequests && app_.getLedgerMaster().isNewPathRequest();
146
147 // We weren't handling new requests and then
148 // there was a new request
149 if (mustBreak)
150 break;
151 }
152
153 if (mustBreak)
154 { // a new request came in while we were working
155 newRequests = true;
156 }
157 else if (newRequests)
158 { // we only did new requests, so we always need a last pass
159 newRequests = app_.getLedgerMaster().isNewPathRequest();
160 }
161 else
162 { // if there are no new requests, we are done
163 newRequests = app_.getLedgerMaster().isNewPathRequest();
164 if (!newRequests)
165 break;
166 }
167
168 // Hold on to the line cache until after the lock is released, so it can
169 // be destroyed outside of the lock
171 {
172 // Get the latest requests, cache, and ledger for next pass
174
175 if (requests_.empty())
176 break;
177 requests = requests_;
178 lastCache = cache;
179 cache = getLineCache(cache->getLedger(), false);
180 }
181 } while (!app_.getJobQueue().isStopping());
182
183 JLOG(mJournal.debug()) << "updateAll complete: " << processed << " processed and " << removed << " removed";
184}
185
186bool
188{
190 return !requests_.empty();
191}
192
193void
195{
197
198 // Insert after any older unserviced requests but before
199 // any serviced requests
200 auto ret = std::find_if(requests_.begin(), requests_.end(), [](auto const& wl) {
201 auto r = wl.lock();
202
203 // We come before handled requests
204 return r && !r->isNew();
205 });
206
207 requests_.emplace(ret, req);
208}
209
210// Make a new-style path_find request
213 std::shared_ptr<InfoSub> const& subscriber,
214 std::shared_ptr<ReadView const> const& inLedger,
215 Json::Value const& requestJson)
216{
217 auto req = std::make_shared<PathRequest>(app_, subscriber, ++mLastIdentifier, *this, mJournal);
218
219 auto [valid, jvRes] = req->doCreate(getLineCache(inLedger, false), requestJson);
220
221 if (valid)
222 {
223 subscriber->setRequest(req);
226 }
227 return std::move(jvRes);
228}
229
230// Make an old-style ripple_path_find request
234 std::function<void(void)> completion,
235 Resource::Consumer& consumer,
236 std::shared_ptr<ReadView const> const& inLedger,
237 Json::Value const& request)
238{
239 // This assignment must take place before the
240 // completion function is called
241 req = std::make_shared<PathRequest>(app_, completion, consumer, ++mLastIdentifier, *this, mJournal);
242
243 auto [valid, jvRes] = req->doCreate(getLineCache(inLedger, false), request);
244
245 if (!valid)
246 {
247 req.reset();
248 }
249 else
250 {
253 {
254 // The newPathRequest failed. Tell the caller.
255 jvRes = rpcError(rpcTOO_BUSY);
256 req.reset();
257 }
258 }
259
260 return std::move(jvRes);
261}
262
265 Resource::Consumer& consumer,
266 std::shared_ptr<ReadView const> const& inLedger,
267 Json::Value const& request)
268{
269 auto cache = std::make_shared<RippleLineCache>(inLedger, app_.journal("RippleLineCache"));
270
271 auto req = std::make_shared<PathRequest>(app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal);
272
273 auto [valid, jvRes] = req->doCreate(cache, request);
274 if (valid)
275 jvRes = req->doUpdate(cache, false);
276 return std::move(jvRes);
277}
278
279} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
virtual LedgerMaster & getLedgerMaster()=0
virtual beast::Journal journal(std::string const &name)=0
virtual JobQueue & getJobQueue()=0
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:143
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:17
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:6
@ jtPATH_FIND
Definition Job.h:64
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
@ rpcTOO_BUSY
Definition ErrorCodes.h:37
T remove_if(T... args)
T reset(T... args)
T size(T... args)