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