mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Better suspend for continuation
This commit is contained in:
committed by
Nik Bougalis
parent
b0a855a10e
commit
545b2fd6b1
@@ -42,20 +42,10 @@ struct Context
|
|||||||
LedgerMaster& ledgerMaster;
|
LedgerMaster& ledgerMaster;
|
||||||
Role role;
|
Role role;
|
||||||
InfoSub::pointer infoSub;
|
InfoSub::pointer infoSub;
|
||||||
Suspend suspend;
|
JobQueueSuspender suspend;
|
||||||
Callback yield;
|
|
||||||
NodeStore::ScopedMetrics metrics;
|
NodeStore::ScopedMetrics metrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
|
||||||
void suspend(Context const& context, Continuation const& continuation)
|
|
||||||
{
|
|
||||||
if (context.suspend)
|
|
||||||
context.suspend(continuation);
|
|
||||||
else
|
|
||||||
continuation(doNothingCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // RPC
|
} // RPC
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
|
|||||||
@@ -25,27 +25,15 @@
|
|||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
/** SuspendCallback: a function that a Coroutine gives to the coroutine
|
/** Coroutine is a function that is given to the coroutine scheduler which
|
||||||
scheduler so that it gets a callback with a Suspend when it runs.
|
later gets called with a Suspend. A Coroutine can't be empty. */
|
||||||
*/
|
using Coroutine = std::function <void (Suspend const&)>;
|
||||||
using SuspendCallback = std::function <void (Suspend const&)>;
|
|
||||||
|
|
||||||
/** Runs a function that takes a SuspendCallback as a coroutine. */
|
/** Run as a coroutine. */
|
||||||
class Coroutine
|
void runOnCoroutine(Coroutine const&);
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Coroutine (SuspendCallback const&);
|
|
||||||
~Coroutine();
|
|
||||||
|
|
||||||
/** Run the coroutine and guarantee completion. */
|
/** Run as coroutine if UseCoroutines::yes, otherwise run immediately. */
|
||||||
void run ();
|
void runOnCoroutine(UseCoroutines, Coroutine const&);
|
||||||
|
|
||||||
private:
|
|
||||||
struct Impl;
|
|
||||||
std::shared_ptr <Impl> impl_;
|
|
||||||
|
|
||||||
Coroutine (std::shared_ptr <Impl> const&);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // RPC
|
} // RPC
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace:
|
|||||||
using Callback = std::function <void ()>;
|
using Callback = std::function <void ()>;
|
||||||
using Continuation = std::function <void (Callback const&)>;
|
using Continuation = std::function <void (Callback const&)>;
|
||||||
using Suspend = std::function <void (Continuation const&)>;
|
using Suspend = std::function <void (Continuation const&)>;
|
||||||
using SuspendCallback = std::function <void (Suspend const&)>;
|
using Coroutine = std::function <void (Suspend const&)>;
|
||||||
|
|
||||||
A `Callback` is a generic 0-argument function. A given `Callback` might or might
|
A `Callback` is a generic 0-argument function. A given `Callback` might or might
|
||||||
not block. Unless otherwise advised, do not hold locks or any resource that
|
not block. Unless otherwise advised, do not hold locks or any resource that
|
||||||
@@ -36,7 +36,7 @@ A `Suspend` is a function belonging to a `Coroutine`. A `Suspend` runs a
|
|||||||
`Continuation`, passing it a `Callback` that continues execution of the
|
`Continuation`, passing it a `Callback` that continues execution of the
|
||||||
`Coroutine`.
|
`Coroutine`.
|
||||||
|
|
||||||
And finally, a `SuspendCallback` is a `std::function` which is given a
|
And finally, a `Coroutine` is a `std::function` which is given a
|
||||||
`Suspend`. This is what the RPC handler gives to the coroutine manager,
|
`Suspend`. This is what the RPC handler gives to the coroutine manager,
|
||||||
expecting to get called back with a `Suspend` and to be able to start execution.
|
expecting to get called back with a `Suspend` and to be able to start execution.
|
||||||
|
|
||||||
@@ -47,10 +47,10 @@ straight-forward.
|
|||||||
|
|
||||||
1. The instance of `ServerHandler` receives an RPC request.
|
1. The instance of `ServerHandler` receives an RPC request.
|
||||||
|
|
||||||
2. It creates a `SuspendCallback` and gives it to the coroutine manager.
|
2. It creates a `Coroutine` and gives it to the coroutine manager.
|
||||||
|
|
||||||
3. The coroutine manager creates a `Coroutine`, starts it up, and then calls
|
3. The coroutine manager creates a `Coroutine`, starts it up, and then calls
|
||||||
the `SuspendCallback` with a `Suspend`.
|
the `Coroutine` with a `Suspend`.
|
||||||
|
|
||||||
4. Now the RPC response starts to be calculated.
|
4. Now the RPC response starts to be calculated.
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
class BasicConfig;
|
||||||
class JobQueue;
|
class JobQueue;
|
||||||
class Section;
|
class Section;
|
||||||
|
|
||||||
@@ -37,19 +38,21 @@ namespace RPC {
|
|||||||
the RPC yield mechanism works.
|
the RPC yield mechanism works.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Callback: do something and eventually return. */
|
/** Callback: do something and eventually return. Can't be empty. */
|
||||||
using Callback = std::function <void ()>;
|
using Callback = std::function <void ()>;
|
||||||
|
|
||||||
/** A non-empty callback that does nothing. */
|
/** Continuation: do something, guarantee to eventually call Callback.
|
||||||
static
|
Can't be empty. */
|
||||||
Callback const doNothingCallback ([] () {});
|
|
||||||
|
|
||||||
/** Continuation: do something, guarantee to eventually call Callback. */
|
|
||||||
using Continuation = std::function <void (Callback const&)>;
|
using Continuation = std::function <void (Callback const&)>;
|
||||||
|
|
||||||
/** Suspend: suspend execution, pending completion of a Continuation. */
|
/** Suspend: suspend execution, pending completion of a Continuation.
|
||||||
|
Can't be empty. */
|
||||||
using Suspend = std::function <void (Continuation const&)>;
|
using Suspend = std::function <void (Continuation const&)>;
|
||||||
|
|
||||||
|
/** A non-empty Suspend that immediately calls its callback. */
|
||||||
|
extern
|
||||||
|
Suspend const dontSuspend;
|
||||||
|
|
||||||
/** Wrap an Output so it yields after approximately `chunkSize` bytes.
|
/** Wrap an Output so it yields after approximately `chunkSize` bytes.
|
||||||
|
|
||||||
chunkedYieldingOutput() only yields after a call to output(), so there might
|
chunkedYieldingOutput() only yields after a call to output(), so there might
|
||||||
@@ -75,11 +78,12 @@ private:
|
|||||||
Callback const yield_;
|
Callback const yield_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class UseCoroutines {no, yes};
|
||||||
|
|
||||||
/** When do we yield when performing a ledger computation? */
|
/** When do we yield when performing a ledger computation? */
|
||||||
struct YieldStrategy
|
struct YieldStrategy
|
||||||
{
|
{
|
||||||
enum class Streaming {no, yes};
|
enum class Streaming {no, yes};
|
||||||
enum class UseCoroutines {no, yes};
|
|
||||||
|
|
||||||
/** Is the data streamed, or generated monolithically? */
|
/** Is the data streamed, or generated monolithically? */
|
||||||
Streaming streaming = Streaming::no;
|
Streaming streaming = Streaming::no;
|
||||||
@@ -88,10 +92,6 @@ struct YieldStrategy
|
|||||||
never yield. */
|
never yield. */
|
||||||
UseCoroutines useCoroutines = UseCoroutines::no;
|
UseCoroutines useCoroutines = UseCoroutines::no;
|
||||||
|
|
||||||
/** How many bytes do we emit before yielding? 0 means "never yield due to
|
|
||||||
number of bytes sent". */
|
|
||||||
std::size_t byteYieldCount = 0;
|
|
||||||
|
|
||||||
/** How many accounts do we process before yielding? 0 means "never yield
|
/** How many accounts do we process before yielding? 0 means "never yield
|
||||||
due to number of accounts processed." */
|
due to number of accounts processed." */
|
||||||
std::size_t accountYieldCount = 0;
|
std::size_t accountYieldCount = 0;
|
||||||
@@ -101,23 +101,32 @@ struct YieldStrategy
|
|||||||
std::size_t transactionYieldCount = 0;
|
std::size_t transactionYieldCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Create a yield strategy from a configuration Section. */
|
/** Does a BasicConfig require the use of coroutines? */
|
||||||
YieldStrategy makeYieldStrategy (Section const&);
|
UseCoroutines useCoroutines(BasicConfig const&);
|
||||||
|
|
||||||
/** Return a continuation that runs a Callback on a Job Queue with a given
|
/** Create a yield strategy from a BasicConfig. */
|
||||||
name and JobType. */
|
YieldStrategy makeYieldStrategy(BasicConfig const&);
|
||||||
Continuation callbackOnJobQueue (
|
|
||||||
JobQueue&, std::string const& jobName, JobType);
|
|
||||||
|
|
||||||
/** Return a Callback that will suspend and then run a continuation. */
|
/** JobQueueSuspender is a suspend, with a yield that reschedules the job
|
||||||
inline
|
on the job queue. */
|
||||||
Callback suspendForContinuation (
|
struct JobQueueSuspender
|
||||||
Suspend const& suspend, Continuation const& continuation)
|
|
||||||
{
|
{
|
||||||
return suspend
|
/** Possibly suspend current execution. */
|
||||||
? Callback ([=] () { suspend (continuation); })
|
Suspend const suspend;
|
||||||
: Callback ([=] () { continuation (doNothingCallback); });
|
|
||||||
}
|
/** Possibly yield and restart on the job queue. */
|
||||||
|
Callback const yield;
|
||||||
|
|
||||||
|
/** Create a JobQueueSuspender where yield does nothing and the suspend
|
||||||
|
immediately executes the continuation. */
|
||||||
|
JobQueueSuspender();
|
||||||
|
|
||||||
|
/** Create a JobQueueSuspender with a Suspend.
|
||||||
|
|
||||||
|
When yield is called, it reschedules the current job on the JobQueue
|
||||||
|
with the given jobName. */
|
||||||
|
JobQueueSuspender(Suspend const&, std::string const& jobName);
|
||||||
|
};
|
||||||
|
|
||||||
} // RPC
|
} // RPC
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <ripple/protocol/JsonFields.h>
|
#include <ripple/protocol/JsonFields.h>
|
||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/Status.h>
|
#include <ripple/rpc/Status.h>
|
||||||
|
#include <ripple/rpc/Yield.h>
|
||||||
#include <ripple/rpc/impl/Handler.h>
|
#include <ripple/rpc/impl/Handler.h>
|
||||||
#include <ripple/server/Role.h>
|
#include <ripple/server/Role.h>
|
||||||
|
|
||||||
@@ -86,15 +87,15 @@ private:
|
|||||||
template <class Object>
|
template <class Object>
|
||||||
void LedgerHandler::writeResult (Object& value)
|
void LedgerHandler::writeResult (Object& value)
|
||||||
{
|
{
|
||||||
|
auto& yield = context_.suspend.yield;
|
||||||
if (ledger_)
|
if (ledger_)
|
||||||
{
|
{
|
||||||
Json::copyFrom (value, result_);
|
Json::copyFrom (value, result_);
|
||||||
addJson (value, {*ledger_, options_, context_.yield});
|
addJson (value, {*ledger_, options_, yield});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto& master = getApp().getLedgerMaster ();
|
auto& master = getApp().getLedgerMaster ();
|
||||||
auto& yield = context_.yield;
|
|
||||||
{
|
{
|
||||||
auto&& closed = Json::addObject (value, jss::closed);
|
auto&& closed = Json::addObject (value, jss::closed);
|
||||||
addJson (closed, {*master.getClosedLedger(), 0, yield});
|
addJson (closed, {*master.getClosedLedger(), 0, yield});
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
|
|||||||
|
|
||||||
Json::Value jvResult;
|
Json::Value jvResult;
|
||||||
|
|
||||||
if (! context.suspend ||
|
if (getConfig().RUN_STANDALONE ||
|
||||||
getConfig().RUN_STANDALONE ||
|
|
||||||
context.params.isMember(jss::ledger) ||
|
context.params.isMember(jss::ledger) ||
|
||||||
context.params.isMember(jss::ledger_index) ||
|
context.params.isMember(jss::ledger_index) ||
|
||||||
context.params.isMember(jss::ledger_hash))
|
context.params.isMember(jss::ledger_hash))
|
||||||
@@ -105,15 +104,13 @@ Json::Value doRipplePathFind (RPC::Context& context)
|
|||||||
lpLedger = context.ledgerMaster.getClosedLedger();
|
lpLedger = context.ledgerMaster.getClosedLedger();
|
||||||
|
|
||||||
PathRequest::pointer request;
|
PathRequest::pointer request;
|
||||||
suspend(context,
|
context.suspend.suspend(
|
||||||
[&request, &context, &jvResult, &lpLedger]
|
[&request, &context, &jvResult, &lpLedger]
|
||||||
(RPC::Callback const& callback)
|
(RPC::Callback const& callback)
|
||||||
{
|
{
|
||||||
jvResult = getApp().getPathRequests().makeLegacyPathRequest (
|
jvResult = getApp().getPathRequests().makeLegacyPathRequest (
|
||||||
request, callback, lpLedger, context.params);
|
request, callback, lpLedger, context.params);
|
||||||
assert(callback);
|
callback();
|
||||||
if (! request && callback)
|
|
||||||
callback();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (request)
|
if (request)
|
||||||
|
|||||||
@@ -24,60 +24,56 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
namespace {
|
||||||
|
|
||||||
using CoroutineType = Continuation;
|
using CoroutineType = Continuation;
|
||||||
using CoroutinePull = boost::coroutines::coroutine <CoroutineType>::pull_type;
|
using BoostCoroutine = boost::coroutines::asymmetric_coroutine<CoroutineType>;
|
||||||
using CoroutinePush = boost::coroutines::coroutine <CoroutineType>::push_type;
|
using Pull = BoostCoroutine::pull_type;
|
||||||
|
using Push = BoostCoroutine::push_type;
|
||||||
|
|
||||||
struct Coroutine::Impl : public std::enable_shared_from_this <Coroutine::Impl>
|
void runOnCoroutineImpl(std::shared_ptr<Pull> pull)
|
||||||
{
|
{
|
||||||
Impl (CoroutinePull&& pull_) : pull (std::move (pull_))
|
while (*pull)
|
||||||
{
|
{
|
||||||
}
|
(*pull)();
|
||||||
|
|
||||||
CoroutinePull pull;
|
if (! *pull)
|
||||||
|
return;
|
||||||
|
|
||||||
void run()
|
if (auto continuation = pull->get())
|
||||||
{
|
|
||||||
while (pull)
|
|
||||||
{
|
{
|
||||||
pull();
|
continuation ([pull] () { runOnCoroutineImpl(pull); });
|
||||||
|
return;
|
||||||
if (! pull)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (auto continuation = pull.get())
|
|
||||||
{
|
|
||||||
auto that = shared_from_this();
|
|
||||||
continuation ([that] () { that->run(); });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Coroutine::Coroutine (SuspendCallback const& suspendCallback)
|
|
||||||
{
|
|
||||||
CoroutinePull pull ([suspendCallback] (CoroutinePush& push)
|
|
||||||
{
|
|
||||||
Suspend suspend = [&push] (CoroutineType const& cbc) {
|
|
||||||
push (cbc);
|
|
||||||
};
|
|
||||||
suspend ({});
|
|
||||||
suspendCallback (suspend);
|
|
||||||
});
|
|
||||||
|
|
||||||
impl_ = std::make_shared<Impl> (std::move (pull));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Coroutine::~Coroutine() = default;
|
} // namespace
|
||||||
|
|
||||||
void Coroutine::run()
|
void runOnCoroutine(Coroutine const& coroutine)
|
||||||
{
|
{
|
||||||
assert (impl_);
|
auto pullFunction = [coroutine] (Push& push) {
|
||||||
if (impl_)
|
Suspend suspend = [&push] (CoroutineType const& cbc) {
|
||||||
impl_->run();
|
if (push)
|
||||||
impl_.reset();
|
push (cbc);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run once doing nothing, to get the other side started.
|
||||||
|
suspend([] (Callback const& callback) { callback(); });
|
||||||
|
|
||||||
|
// Now run the coroutine.
|
||||||
|
coroutine(suspend);
|
||||||
|
};
|
||||||
|
|
||||||
|
runOnCoroutineImpl(std::make_shared<Pull>(pullFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
void runOnCoroutine(UseCoroutines useCoroutines, Coroutine const& coroutine)
|
||||||
|
{
|
||||||
|
if (useCoroutines == UseCoroutines::yes)
|
||||||
|
runOnCoroutine(coroutine);
|
||||||
|
else
|
||||||
|
coroutine(dontSuspend);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // RPC
|
} // RPC
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/basics/BasicConfig.h>
|
#include <ripple/basics/BasicConfig.h>
|
||||||
#include <ripple/rpc/Yield.h>
|
#include <ripple/rpc/Yield.h>
|
||||||
#include <ripple/rpc/tests/TestOutputSuite.test.h>
|
#include <ripple/rpc/tests/TestOutputSuite.test.h>
|
||||||
@@ -25,6 +26,34 @@
|
|||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
|
static
|
||||||
|
UseCoroutines defaultUseCoroutines = UseCoroutines::no;
|
||||||
|
|
||||||
|
Suspend const dontSuspend = [] (Continuation const& continuation)
|
||||||
|
{
|
||||||
|
continuation([] () {});
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void runOnJobQueue(std::string const& name, Callback const& callback)
|
||||||
|
{
|
||||||
|
boost::function <void (Job&)> cb([callback] (Job&) { callback(); });
|
||||||
|
getApp().getJobQueue().addJob(jtCLIENT, name, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
Callback suspendForJobQueue(Suspend const& suspend, std::string const& jobName)
|
||||||
|
{
|
||||||
|
assert(suspend);
|
||||||
|
return Callback( [suspend, jobName] () {
|
||||||
|
suspend([jobName] (Callback const& callback) {
|
||||||
|
runOnJobQueue(jobName, callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Json::Output chunkedYieldingOutput (
|
Json::Output chunkedYieldingOutput (
|
||||||
Json::Output const& output, Callback const& yield, std::size_t chunkSize)
|
Json::Output const& output, Callback const& yield, std::size_t chunkSize)
|
||||||
{
|
{
|
||||||
@@ -44,7 +73,6 @@ Json::Output chunkedYieldingOutput (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CountedYield::CountedYield (std::size_t yieldCount, Callback const& yield)
|
CountedYield::CountedYield (std::size_t yieldCount, Callback const& yield)
|
||||||
: yieldCount_ (yieldCount), yield_ (yield)
|
: yieldCount_ (yieldCount), yield_ (yield)
|
||||||
{
|
{
|
||||||
@@ -52,10 +80,7 @@ CountedYield::CountedYield (std::size_t yieldCount, Callback const& yield)
|
|||||||
|
|
||||||
void CountedYield::yield()
|
void CountedYield::yield()
|
||||||
{
|
{
|
||||||
if (!yield_)
|
if (yieldCount_ && yield_)
|
||||||
return;
|
|
||||||
|
|
||||||
if (yieldCount_)
|
|
||||||
{
|
{
|
||||||
if (++count_ >= yieldCount_)
|
if (++count_ >= yieldCount_)
|
||||||
{
|
{
|
||||||
@@ -65,28 +90,38 @@ void CountedYield::yield()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
YieldStrategy makeYieldStrategy (Section const& s)
|
UseCoroutines useCoroutines(BasicConfig const& config)
|
||||||
{
|
{
|
||||||
|
if (auto use = config["section"].get<bool>("use_coroutines"))
|
||||||
|
return *use ? UseCoroutines::yes : UseCoroutines::no;
|
||||||
|
return defaultUseCoroutines;
|
||||||
|
}
|
||||||
|
|
||||||
|
YieldStrategy makeYieldStrategy (BasicConfig const& config)
|
||||||
|
{
|
||||||
|
auto s = config["section"];
|
||||||
YieldStrategy ys;
|
YieldStrategy ys;
|
||||||
ys.streaming = get<bool> (s, "streaming") ?
|
ys.streaming = get<bool> (s, "streaming") ?
|
||||||
YieldStrategy::Streaming::yes :
|
YieldStrategy::Streaming::yes :
|
||||||
YieldStrategy::Streaming::no;
|
YieldStrategy::Streaming::no;
|
||||||
ys.useCoroutines = get<bool> (s, "use_coroutines") ?
|
ys.useCoroutines = useCoroutines(config);
|
||||||
YieldStrategy::UseCoroutines::yes :
|
|
||||||
YieldStrategy::UseCoroutines::no;
|
|
||||||
ys.byteYieldCount = get<std::size_t> (s, "byte_yield_count");
|
|
||||||
ys.accountYieldCount = get<std::size_t> (s, "account_yield_count");
|
ys.accountYieldCount = get<std::size_t> (s, "account_yield_count");
|
||||||
ys.transactionYieldCount = get<std::size_t> (s, "transaction_yield_count");
|
ys.transactionYieldCount = get<std::size_t> (s, "transaction_yield_count");
|
||||||
|
|
||||||
return ys;
|
return ys;
|
||||||
}
|
}
|
||||||
|
|
||||||
Continuation callbackOnJobQueue (
|
JobQueueSuspender::JobQueueSuspender(
|
||||||
JobQueue& jobQueue, std::string const& name, JobType jobType)
|
Suspend const& susp, std::string const& jobName)
|
||||||
|
: suspend(susp ? susp : dontSuspend),
|
||||||
|
yield(suspendForJobQueue(suspend, jobName))
|
||||||
|
{
|
||||||
|
// There's a non-empty jobName exactly if there's a non-empty Suspend.
|
||||||
|
assert(!(susp && jobName.empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
JobQueueSuspender::JobQueueSuspender() : JobQueueSuspender({}, {})
|
||||||
{
|
{
|
||||||
return Continuation ([name, jobType, &jobQueue] (Callback const& cb) {
|
|
||||||
jobQueue.addJob (jobType, name, [cb] (Job&) { cb(); });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // RPC
|
} // RPC
|
||||||
|
|||||||
@@ -46,10 +46,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Strings result;
|
Strings result;
|
||||||
SuspendCallback suspendCallback ([&] (Suspend const& suspend)
|
Coroutine coroutine ([&] (Suspend const& suspend)
|
||||||
{
|
{
|
||||||
Callback yield = suspendForContinuation (
|
Callback yield ([=] () { suspend (makeContinuation ("*")); });
|
||||||
suspend, makeContinuation ("*"));
|
|
||||||
auto out = chunkedYieldingOutput (output, yield, chunkSize);
|
auto out = chunkedYieldingOutput (output, yield, chunkSize);
|
||||||
out ("hello ");
|
out ("hello ");
|
||||||
result.push_back (buffer);
|
result.push_back (buffer);
|
||||||
@@ -70,7 +69,7 @@ public:
|
|||||||
result.push_back (buffer);
|
result.push_back (buffer);
|
||||||
});
|
});
|
||||||
|
|
||||||
Coroutine (suspendCallback).run();
|
runOnCoroutine(UseCoroutines::yes, coroutine);
|
||||||
expectCollectionEquals (result, expected);
|
expectCollectionEquals (result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ ServerHandlerImp::ServerHandlerImp (Stoppable& parent,
|
|||||||
, m_networkOPs (networkOPs)
|
, m_networkOPs (networkOPs)
|
||||||
, m_server (HTTP::make_Server(
|
, m_server (HTTP::make_Server(
|
||||||
*this, io_service, deprecatedLogs().journal("Server")))
|
*this, io_service, deprecatedLogs().journal("Server")))
|
||||||
, m_continuation (RPC::callbackOnJobQueue (
|
, m_jobQueue (jobQueue)
|
||||||
jobQueue, "RPC-Coroutine", jtCLIENT))
|
|
||||||
{
|
{
|
||||||
auto const& group (cm.group ("rpc"));
|
auto const& group (cm.group ("rpc"));
|
||||||
rpc_requests_ = group->make_counter ("requests");
|
rpc_requests_ = group->make_counter ("requests");
|
||||||
@@ -180,24 +179,15 @@ ServerHandlerImp::onRequest (HTTP::Session& session)
|
|||||||
|
|
||||||
auto detach = session.detach();
|
auto detach = session.detach();
|
||||||
|
|
||||||
if (setup_.yieldStrategy.useCoroutines ==
|
// We can copy `this` because ServerHandlerImp is a long-lasting singleton.
|
||||||
RPC::YieldStrategy::UseCoroutines::yes)
|
auto job = [this, detach] (Job&) {
|
||||||
{
|
RPC::runOnCoroutine(
|
||||||
RPC::SuspendCallback suspend (
|
setup_.yieldStrategy.useCoroutines,
|
||||||
[this, detach] (RPC::Suspend const& suspend) {
|
[this, detach] (RPC::Suspend const& suspend) {
|
||||||
processSession (detach, suspend);
|
processSession(detach, suspend);
|
||||||
});
|
});
|
||||||
RPC::Coroutine coroutine (suspend);
|
};
|
||||||
coroutine.run();
|
m_jobQueue.addJob(jtCLIENT, "RPC-Client", job);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getApp().getJobQueue().addJob (
|
|
||||||
jtCLIENT, "RPC-Client",
|
|
||||||
[=] (Job&) {
|
|
||||||
processSession (detach, RPC::Suspend());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -240,15 +230,9 @@ ServerHandlerImp::processRequest (
|
|||||||
Output&& output,
|
Output&& output,
|
||||||
Suspend const& suspend)
|
Suspend const& suspend)
|
||||||
{
|
{
|
||||||
auto yield = RPC::suspendForContinuation (suspend, m_continuation);
|
|
||||||
|
|
||||||
// Move off the webserver thread onto the JobQueue.
|
// Move off the webserver thread onto the JobQueue.
|
||||||
yield();
|
|
||||||
assert (getApp().getJobQueue().getJobForThread());
|
assert (getApp().getJobQueue().getJobForThread());
|
||||||
|
|
||||||
if (auto count = setup_.yieldStrategy.byteYieldCount)
|
|
||||||
output = RPC::chunkedYieldingOutput (std::move (output), yield, count);
|
|
||||||
|
|
||||||
Json::Value jsonRPC;
|
Json::Value jsonRPC;
|
||||||
{
|
{
|
||||||
Json::Reader reader;
|
Json::Reader reader;
|
||||||
@@ -267,7 +251,6 @@ ServerHandlerImp::processRequest (
|
|||||||
// VFALCO NOTE Except that "id" isn't included in the following errors.
|
// VFALCO NOTE Except that "id" isn't included in the following errors.
|
||||||
//
|
//
|
||||||
Json::Value const& id = jsonRPC ["id"];
|
Json::Value const& id = jsonRPC ["id"];
|
||||||
|
|
||||||
Json::Value const& method = jsonRPC ["method"];
|
Json::Value const& method = jsonRPC ["method"];
|
||||||
|
|
||||||
if (! method) {
|
if (! method) {
|
||||||
@@ -365,9 +348,11 @@ ServerHandlerImp::processRequest (
|
|||||||
<< "doRpcCommand:" << strMethod << ":" << params;
|
<< "doRpcCommand:" << strMethod << ":" << params;
|
||||||
|
|
||||||
auto const start (std::chrono::high_resolution_clock::now ());
|
auto const start (std::chrono::high_resolution_clock::now ());
|
||||||
|
|
||||||
RPC::Context context {
|
RPC::Context context {
|
||||||
params, loadType, m_networkOPs, getApp().getLedgerMaster(), role,
|
params, loadType, m_networkOPs, getApp().getLedgerMaster(), role,
|
||||||
nullptr, std::move (suspend), std::move (yield)};
|
nullptr, {suspend, "RPC-Coroutine"}};
|
||||||
|
|
||||||
std::string response;
|
std::string response;
|
||||||
|
|
||||||
if (setup_.yieldStrategy.streaming == RPC::YieldStrategy::Streaming::yes)
|
if (setup_.yieldStrategy.streaming == RPC::YieldStrategy::Streaming::yes)
|
||||||
@@ -755,11 +740,12 @@ setup_Overlay (ServerHandler::Setup& setup)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerHandler::Setup
|
ServerHandler::Setup
|
||||||
setup_ServerHandler (BasicConfig const& config, std::ostream& log)
|
setup_ServerHandler(BasicConfig const& config, std::ostream& log)
|
||||||
{
|
{
|
||||||
ServerHandler::Setup setup;
|
ServerHandler::Setup setup;
|
||||||
setup.ports = detail::parse_Ports (config, log);
|
setup.ports = detail::parse_Ports(config, log);
|
||||||
setup.yieldStrategy = RPC::makeYieldStrategy (config["server"]);
|
setup.yieldStrategy = RPC::makeYieldStrategy(config);
|
||||||
|
|
||||||
detail::setup_Client(setup);
|
detail::setup_Client(setup);
|
||||||
detail::setup_Overlay(setup);
|
detail::setup_Overlay(setup);
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ private:
|
|||||||
beast::Journal m_journal;
|
beast::Journal m_journal;
|
||||||
NetworkOPs& m_networkOPs;
|
NetworkOPs& m_networkOPs;
|
||||||
std::unique_ptr<HTTP::Server> m_server;
|
std::unique_ptr<HTTP::Server> m_server;
|
||||||
RPC::Continuation m_continuation;
|
|
||||||
Setup setup_;
|
Setup setup_;
|
||||||
|
JobQueue& m_jobQueue;
|
||||||
beast::insight::Counter rpc_requests_;
|
beast::insight::Counter rpc_requests_;
|
||||||
beast::insight::Event rpc_io_;
|
beast::insight::Event rpc_io_;
|
||||||
beast::insight::Event rpc_size_;
|
beast::insight::Event rpc_size_;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
#include <ripple/basics/CountedObject.h>
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/core/Config.h>
|
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
#include <ripple/net/InfoSub.h>
|
#include <ripple/net/InfoSub.h>
|
||||||
#include <ripple/net/RPCErr.h>
|
#include <ripple/net/RPCErr.h>
|
||||||
@@ -32,10 +31,12 @@
|
|||||||
#include <ripple/protocol/JsonFields.h>
|
#include <ripple/protocol/JsonFields.h>
|
||||||
#include <ripple/resource/Fees.h>
|
#include <ripple/resource/Fees.h>
|
||||||
#include <ripple/resource/ResourceManager.h>
|
#include <ripple/resource/ResourceManager.h>
|
||||||
|
#include <ripple/rpc/Coroutine.h>
|
||||||
#include <ripple/rpc/RPCHandler.h>
|
#include <ripple/rpc/RPCHandler.h>
|
||||||
#include <ripple/server/Port.h>
|
#include <ripple/server/Port.h>
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
#include <ripple/rpc/RPCHandler.h>
|
#include <ripple/rpc/RPCHandler.h>
|
||||||
|
#include <ripple/rpc/Yield.h>
|
||||||
#include <ripple/server/Role.h>
|
#include <ripple/server/Role.h>
|
||||||
#include <ripple/websocket/WebSocket.h>
|
#include <ripple/websocket/WebSocket.h>
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ public:
|
|||||||
message_ptr getMessage ();
|
message_ptr getMessage ();
|
||||||
bool checkMessage ();
|
bool checkMessage ();
|
||||||
void returnMessage (message_ptr const&);
|
void returnMessage (message_ptr const&);
|
||||||
Json::Value invokeCommand (Json::Value& jvRequest);
|
Json::Value invokeCommand (Json::Value const& jvRequest, RPC::Suspend const&);
|
||||||
|
|
||||||
// Generically implemented per version.
|
// Generically implemented per version.
|
||||||
void setPingTimer ();
|
void setPingTimer ();
|
||||||
@@ -226,7 +227,8 @@ void ConnectionImpl <WebSocket>::returnMessage (message_ptr const& ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class WebSocket>
|
template <class WebSocket>
|
||||||
Json::Value ConnectionImpl <WebSocket>::invokeCommand (Json::Value& jvRequest)
|
Json::Value ConnectionImpl <WebSocket>::invokeCommand (
|
||||||
|
Json::Value const& jvRequest, RPC::Suspend const& suspend)
|
||||||
{
|
{
|
||||||
if (getConsumer().disconnect ())
|
if (getConsumer().disconnect ())
|
||||||
{
|
{
|
||||||
@@ -269,7 +271,8 @@ Json::Value ConnectionImpl <WebSocket>::invokeCommand (Json::Value& jvRequest)
|
|||||||
{
|
{
|
||||||
RPC::Context context {
|
RPC::Context context {
|
||||||
jvRequest, loadType, m_netOPs, getApp().getLedgerMaster(), role,
|
jvRequest, loadType, m_netOPs, getApp().getLedgerMaster(), role,
|
||||||
std::dynamic_pointer_cast<InfoSub> (this->shared_from_this ())};
|
this->shared_from_this (),
|
||||||
|
{suspend, "WSClient::command"}};
|
||||||
RPC::doCommand (context, jvResult[jss::result]);
|
RPC::doCommand (context, jvResult[jss::result]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ beast::IP::Endpoint makeBeastEndpoint (beast::IP::Endpoint const& e)
|
|||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <class WebSocket>
|
template <class WebSocket>
|
||||||
class HandlerImpl
|
class HandlerImpl
|
||||||
: public WebSocket::Handler
|
: public WebSocket::Handler
|
||||||
@@ -293,7 +294,14 @@ public:
|
|||||||
getApp().getJobQueue ().addJob (
|
getApp().getJobQueue ().addJob (
|
||||||
jtCLIENT,
|
jtCLIENT,
|
||||||
"WSClient::destroy",
|
"WSClient::destroy",
|
||||||
std::bind (&ConnectionImpl <WebSocket>::destroy, ptr));
|
[ptr] (Job&) { ConnectionImpl <WebSocket>::destroy(ptr); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void message_job(std::string const& name,
|
||||||
|
connection_ptr const& cpClient)
|
||||||
|
{
|
||||||
|
auto msgs = [this, cpClient] (Job& j) { do_messages(j, cpClient); };
|
||||||
|
getApp().getJobQueue ().addJob (jtCLIENT, "WSClient::" + name, msgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_message (connection_ptr cpClient, message_ptr mpMessage) override
|
void on_message (connection_ptr cpClient, message_ptr mpMessage) override
|
||||||
@@ -327,9 +335,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bRunQ)
|
if (bRunQ)
|
||||||
getApp().getJobQueue ().addJob (jtCLIENT, "WSClient::command",
|
message_job("command", cpClient);
|
||||||
std::bind (&HandlerImpl <WebSocket>::do_messages,
|
|
||||||
this, std::placeholders::_1, cpClient));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_messages (Job& job, connection_ptr const& cpClient)
|
void do_messages (Job& job, connection_ptr const& cpClient)
|
||||||
@@ -364,17 +370,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ptr->checkMessage ())
|
if (ptr->checkMessage ())
|
||||||
getApp().getJobQueue ().addJob (
|
message_job("more", cpClient);
|
||||||
jtCLIENT, "WSClient::more",
|
|
||||||
std::bind (&HandlerImpl <WebSocket>::do_messages, this,
|
|
||||||
std::placeholders::_1, cpClient));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool do_message (Job& job, const connection_ptr& cpClient,
|
bool do_message (Job& job, const connection_ptr& cpClient,
|
||||||
const wsc_ptr& conn, const message_ptr& mpMessage)
|
const wsc_ptr& conn, const message_ptr& mpMessage)
|
||||||
{
|
{
|
||||||
Json::Value jvRequest;
|
Json::Value jvRequest;
|
||||||
Json::Reader jrReader;
|
Json::Reader jrReader;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -414,19 +417,38 @@ public:
|
|||||||
{
|
{
|
||||||
Json::Value& jCmd = jvRequest[jss::command];
|
Json::Value& jCmd = jvRequest[jss::command];
|
||||||
if (jCmd.isString())
|
if (jCmd.isString())
|
||||||
job.rename (std::string ("WSClient::") + jCmd.asString());
|
job.rename ("WSClient::" + jCmd.asString());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const start (std::chrono::high_resolution_clock::now ());
|
auto const start = std::chrono::high_resolution_clock::now ();
|
||||||
Json::Value const jvObj (conn->invokeCommand (jvRequest));
|
|
||||||
std::string const buffer (to_string (jvObj));
|
struct HandlerCoroutineData
|
||||||
|
{
|
||||||
|
Json::Value jvRequest;
|
||||||
|
std::string buffer;
|
||||||
|
wsc_ptr conn;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto data = std::make_shared<HandlerCoroutineData>();
|
||||||
|
data->jvRequest = std::move(jvRequest);
|
||||||
|
data->conn = conn;
|
||||||
|
|
||||||
|
auto coroutine = [data] (RPC::Suspend const& suspend) {
|
||||||
|
data->buffer = to_string(
|
||||||
|
data->conn->invokeCommand(data->jvRequest, suspend));
|
||||||
|
};
|
||||||
|
static auto const disableWebsocketsCoroutines = true;
|
||||||
|
auto useCoroutines = disableWebsocketsCoroutines ?
|
||||||
|
RPC::UseCoroutines::no : RPC::useCoroutines(desc_.config);
|
||||||
|
runOnCoroutine(useCoroutines, coroutine);
|
||||||
|
|
||||||
rpc_time_.notify (static_cast <beast::insight::Event::value_type> (
|
rpc_time_.notify (static_cast <beast::insight::Event::value_type> (
|
||||||
std::chrono::duration_cast <std::chrono::milliseconds> (
|
std::chrono::duration_cast <std::chrono::milliseconds> (
|
||||||
std::chrono::high_resolution_clock::now () - start)));
|
std::chrono::high_resolution_clock::now () - start)));
|
||||||
++rpc_requests_;
|
++rpc_requests_;
|
||||||
rpc_size_.notify (static_cast <beast::insight::Event::value_type>
|
rpc_size_.notify (static_cast <beast::insight::Event::value_type>
|
||||||
(buffer.size ()));
|
(data->buffer.size()));
|
||||||
send (cpClient, buffer, false);
|
send (cpClient, data->buffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user