rippled
Loading...
Searching...
No Matches
RipplePathFind.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/ledger/LedgerMaster.h>
21#include <xrpld/app/paths/PathRequests.h>
22#include <xrpld/rpc/Context.h>
23#include <xrpld/rpc/detail/LegacyPathFind.h>
24#include <xrpld/rpc/detail/RPCHelpers.h>
25
26#include <xrpl/protocol/RPCErr.h>
27#include <xrpl/resource/Fees.h>
28
29namespace ripple {
30
31// This interface is deprecated.
34{
35 if (context.app.config().PATH_SEARCH_MAX == 0)
37
39
41 Json::Value jvResult;
42
43 if (!context.app.config().standalone() &&
44 !context.params.isMember(jss::ledger) &&
45 !context.params.isMember(jss::ledger_index) &&
46 !context.params.isMember(jss::ledger_hash))
47 {
48 // No ledger specified, use pathfinding defaults
49 // and dispatch to pathfinding engine
52 {
53 if (context.apiVersion == 1)
54 return rpcError(rpcNO_NETWORK);
55 return rpcError(rpcNOT_SYNCED);
56 }
57
59 lpLedger = context.ledgerMaster.getClosedLedger();
60
61 // It doesn't look like there's much odd happening here, but you should
62 // be aware this code runs in a JobQueue::Coro, which is a coroutine.
63 // And we may be flipping around between threads. Here's an overview:
64 //
65 // 1. We're running doRipplePathFind() due to a call to
66 // ripple_path_find. doRipplePathFind() is currently running
67 // inside of a JobQueue::Coro using a JobQueue thread.
68 //
69 // 2. doRipplePathFind's call to makeLegacyPathRequest() enqueues the
70 // path-finding request. That request will (probably) run at some
71 // indeterminate future time on a (probably different) JobQueue
72 // thread.
73 //
74 // 3. As a continuation from that path-finding JobQueue thread, the
75 // coroutine we're currently running in (!) is posted to the
76 // JobQueue. Because it is a continuation, that post won't
77 // happen until the path-finding request completes.
78 //
79 // 4. Once the continuation is enqueued, and we have reason to think
80 // the path-finding job is likely to run, then the coroutine we're
81 // running in yield()s. That means it surrenders its thread in
82 // the JobQueue. The coroutine is suspended, but ready to run,
83 // because it is kept resident by a shared_ptr in the
84 // path-finding continuation.
85 //
86 // 5. If all goes well then path-finding runs on a JobQueue thread
87 // and executes its continuation. The continuation posts this
88 // same coroutine (!) to the JobQueue.
89 //
90 // 6. When the JobQueue calls this coroutine, this coroutine resumes
91 // from the line below the coro->yield() and returns the
92 // path-finding result.
93 //
94 // With so many moving parts, what could go wrong?
95 //
96 // Just in terms of the JobQueue refusing to add jobs at shutdown
97 // there are two specific things that can go wrong.
98 //
99 // 1. The path-finding Job queued by makeLegacyPathRequest() might be
100 // rejected (because we're shutting down).
101 //
102 // Fortunately this problem can be addressed by looking at the
103 // return value of makeLegacyPathRequest(). If
104 // makeLegacyPathRequest() cannot get a thread to run the path-find
105 // on, then it returns an empty request.
106 //
107 // 2. The path-finding job might run, but the Coro::post() might be
108 // rejected by the JobQueue (because we're shutting down).
109 //
110 // We handle this case by resuming (not posting) the Coro.
111 // By resuming the Coro, we allow the Coro to run to completion
112 // on the current thread instead of requiring that it run on a
113 // new thread from the JobQueue.
114 //
115 // Both of these failure modes are hard to recreate in a unit test
116 // because they are so dependent on inter-thread timing. However
117 // the failure modes can be observed by synchronously (inside the
118 // rippled source code) shutting down the application. The code to
119 // do so looks like this:
120 //
121 // context.app.signalStop();
122 // while (! context.app.getJobQueue().jobCounter().joined()) { }
123 //
124 // The first line starts the process of shutting down the app.
125 // The second line waits until no more jobs can be added to the
126 // JobQueue before letting the thread continue.
127 //
128 // May 2017
129 jvResult = context.app.getPathRequests().makeLegacyPathRequest(
130 request,
131 [&context]() {
132 // Copying the shared_ptr keeps the coroutine alive up
133 // through the return. Otherwise the storage under the
134 // captured reference could evaporate when we return from
135 // coroCopy->resume(). This is not strictly necessary, but
136 // will make maintenance easier.
137 std::shared_ptr<JobQueue::Coro> coroCopy{context.coro};
138 if (!coroCopy->post())
139 {
140 // The post() failed, so we won't get a thread to let
141 // the Coro finish. We'll call Coro::resume() so the
142 // Coro can finish on our thread. Otherwise the
143 // application will hang on shutdown.
144 coroCopy->resume();
145 }
146 },
147 context.consumer,
148 lpLedger,
149 context.params);
150 if (request)
151 {
152 context.coro->yield();
153 jvResult = request->doStatus(context.params);
154 }
155
156 return jvResult;
157 }
158
159 // The caller specified a ledger
160 jvResult = RPC::lookupLedger(lpLedger, context);
161 if (!lpLedger)
162 return jvResult;
163
164 RPC::LegacyPathFind lpf(isUnlimited(context.role), context.app);
165 if (!lpf.isOk())
166 return rpcError(rpcTOO_BUSY);
167
168 auto result = context.app.getPathRequests().doLegacyPathRequest(
169 context.consumer, lpLedger, context.params);
170
171 for (auto& fieldName : jvResult.getMemberNames())
172 result[fieldName] = std::move(jvResult[fieldName]);
173
174 return result;
175}
176
177} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
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
bool standalone() const
Definition Config.h:336
int PATH_SEARCH_MAX
Definition Config.h:198
std::shared_ptr< Ledger const > getClosedLedger()
std::chrono::seconds getValidatedLedgerAge()
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 &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Charge const feeHeavyBurdenRPC
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ rpcNOT_SUPPORTED
Definition ErrorCodes.h:132
@ rpcNO_NETWORK
Definition ErrorCodes.h:66
@ rpcTOO_BUSY
Definition ErrorCodes.h:56
@ rpcNOT_SYNCED
Definition ErrorCodes.h:67
Json::Value rpcError(int iError)
Definition RPCErr.cpp:31
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:125
Json::Value doRipplePathFind(RPC::JsonContext &)
unsigned int apiVersion
Definition Context.h:49
std::shared_ptr< JobQueue::Coro > coro
Definition Context.h:47
Resource::Charge & loadType
Definition Context.h:42
Application & app
Definition Context.h:41
LedgerMaster & ledgerMaster
Definition Context.h:44
Resource::Consumer & consumer
Definition Context.h:45