xrpld
Loading...
Searching...
No Matches
RipplePathFind.cpp
1#include <xrpld/app/ledger/LedgerMaster.h>
2#include <xrpld/rpc/Context.h>
3#include <xrpld/rpc/Role.h>
4#include <xrpld/rpc/detail/LegacyPathFind.h>
5#include <xrpld/rpc/detail/PathRequest.h>
6#include <xrpld/rpc/detail/PathRequestManager.h>
7#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
8#include <xrpld/rpc/detail/Tuning.h>
9
10#include <xrpl/core/JobQueue.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/protocol/ErrorCodes.h>
13#include <xrpl/protocol/RPCErr.h>
14#include <xrpl/protocol/jss.h>
15#include <xrpl/resource/Fees.h>
16
17#include <memory>
18#include <utility>
19
20namespace xrpl {
21
22// This interface is deprecated.
23json::Value
25{
26 if (context.app.config().pathSearchMax == 0)
28
30
32 json::Value jvResult;
33
34 if (!context.app.config().standalone() && !context.params.isMember(jss::ledger) &&
35 !context.params.isMember(jss::ledger_index) && !context.params.isMember(jss::ledger_hash))
36 {
37 // No ledger specified, use pathfinding defaults
38 // and dispatch to pathfinding engine
41 {
42 if (context.apiVersion == 1)
43 return rpcError(RpcNoNetwork);
44 return rpcError(RpcNotSynced);
45 }
46
48 lpLedger = context.ledgerMaster.getClosedLedger();
49
50 // It doesn't look like there's much odd happening here, but you should
51 // be aware this code runs in a JobQueue::Coro, which is a coroutine.
52 // And we may be flipping around between threads. Here's an overview:
53 //
54 // 1. We're running doRipplePathFind() due to a call to
55 // ripple_path_find. doRipplePathFind() is currently running
56 // inside of a JobQueue::Coro using a JobQueue thread.
57 //
58 // 2. doRipplePathFind's call to makeLegacyPathRequest() enqueues the
59 // path-finding request. That request will (probably) run at some
60 // indeterminate future time on a (probably different) JobQueue
61 // thread.
62 //
63 // 3. As a continuation from that path-finding JobQueue thread, the
64 // coroutine we're currently running in (!) is posted to the
65 // JobQueue. Because it is a continuation, that post won't
66 // happen until the path-finding request completes.
67 //
68 // 4. Once the continuation is enqueued, and we have reason to think
69 // the path-finding job is likely to run, then the coroutine we're
70 // running in yield()s. That means it surrenders its thread in
71 // the JobQueue. The coroutine is suspended, but ready to run,
72 // because it is kept resident by a shared_ptr in the
73 // path-finding continuation.
74 //
75 // 5. If all goes well then path-finding runs on a JobQueue thread
76 // and executes its continuation. The continuation posts this
77 // same coroutine (!) to the JobQueue.
78 //
79 // 6. When the JobQueue calls this coroutine, this coroutine resumes
80 // from the line below the coro->yield() and returns the
81 // path-finding result.
82 //
83 // With so many moving parts, what could go wrong?
84 //
85 // Just in terms of the JobQueue refusing to add jobs at shutdown
86 // there are two specific things that can go wrong.
87 //
88 // 1. The path-finding Job queued by makeLegacyPathRequest() might be
89 // rejected (because we're shutting down).
90 //
91 // Fortunately this problem can be addressed by looking at the
92 // return value of makeLegacyPathRequest(). If
93 // makeLegacyPathRequest() cannot get a thread to run the path-find
94 // on, then it returns an empty request.
95 //
96 // 2. The path-finding job might run, but the Coro::post() might be
97 // rejected by the JobQueue (because we're shutting down).
98 //
99 // We handle this case by resuming (not posting) the Coro.
100 // By resuming the Coro, we allow the Coro to run to completion
101 // on the current thread instead of requiring that it run on a
102 // new thread from the JobQueue.
103 //
104 // Both of these failure modes are hard to recreate in a unit test
105 // because they are so dependent on inter-thread timing. However
106 // the failure modes can be observed by synchronously (inside the
107 // xrpld source code) shutting down the application. The code to
108 // do so looks like this:
109 //
110 // context.app.signalStop();
111 // while (! context.app.getJobQueue().jobCounter().joined()) { }
112 //
113 // The first line starts the process of shutting down the app.
114 // The second line waits until no more jobs can be added to the
115 // JobQueue before letting the thread continue.
116 //
117 // May 2017
119 request,
120 [&context]() {
121 // Copying the shared_ptr keeps the coroutine alive up
122 // through the return. Otherwise the storage under the
123 // captured reference could evaporate when we return from
124 // coroCopy->resume(). This is not strictly necessary, but
125 // will make maintenance easier.
126 std::shared_ptr<JobQueue::Coro> const coroCopy{context.coro};
127 if (!coroCopy->post())
128 {
129 // The post() failed, so we won't get a thread to let
130 // the Coro finish. We'll call Coro::resume() so the
131 // Coro can finish on our thread. Otherwise the
132 // application will hang on shutdown.
133 coroCopy->resume();
134 }
135 },
136 context.consumer,
137 lpLedger,
138 context.params);
139 if (request)
140 {
141 context.coro->yield();
142 jvResult = request->doStatus(context.params);
143 }
144
145 return jvResult;
146 }
147
148 // The caller specified a ledger
149 jvResult = RPC::lookupLedger(lpLedger, context);
150 if (!lpLedger)
151 return jvResult;
152
153 RPC::LegacyPathFind const lpf(isUnlimited(context.role), context.app);
154 if (!lpf.isOk())
155 return rpcError(RpcTooBusy);
156
157 auto result = context.app.getPathRequestManager().doLegacyPathRequest(
158 context.consumer, lpLedger, context.params);
159
160 for (auto& fieldName : jvResult.getMemberNames())
161 result[fieldName] = std::move(jvResult[fieldName]);
162
163 return result;
164}
165
166} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Members getMemberNames() const
Return a list of the member names.
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
int pathSearchMax
Definition Config.h:184
bool standalone() const
Definition Config.h:316
std::chrono::seconds getValidatedLedgerAge()
std::shared_ptr< Ledger const > getClosedLedger()
json::Value doLegacyPathRequest(Resource::Consumer &consumer, std::shared_ptr< ReadView const > const &inLedger, 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)
json::Value doStatus(json::Value const &) override
std::shared_ptr< PathRequest > pointer
Definition PathRequest.h:36
virtual PathRequestManager & getPathRequestManager()=0
virtual LedgerMaster & getLedgerMaster()=0
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext const &context, json::Value &result)
Looks up a ledger from a request and fills a json::Value with ledger data.
Charge const kFeeHeavyBurdenRpc
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ RpcNotSupported
Definition ErrorCodes.h:114
@ RpcNoNetwork
Definition ErrorCodes.h:48
@ RpcNotSynced
Definition ErrorCodes.h:49
@ RpcTooBusy
Definition ErrorCodes.h:38
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
json::Value doRipplePathFind(RPC::JsonContext &)
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:115
Application & app
Definition Context.h:21
Resource::Charge & loadType
Definition Context.h:22
Resource::Consumer & consumer
Definition Context.h:25
unsigned int apiVersion
Definition Context.h:29
LedgerMaster & ledgerMaster
Definition Context.h:24
std::shared_ptr< JobQueue::Coro > coro
Definition Context.h:27
json::Value params
Definition Context.h:43