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