* Remove include <ranges> * Formatting fix * Output for subscriptions * Output from sign, submit etc. * Output from ledger * Output from account_tx * Output from transaction_entry * Output from tx * Store close_time_iso in API v2 output * Add small APIv2 unit test for subscribe * Add unit test for transaction_entry * Add unit test for tx * Remove inLedger from API version 2 * Set ledger_hash and ledger_index * Move isValidated from RPCHelpers to LedgerMaster * Store closeTime in LedgerFill * Time formatting fix * additional tests for Subscribe unit tests * Improved comments * Rename mInLedger to mLedgerIndex * Minor fixes * Set ledger_hash on closed ledger, even if not validated * Update API-CHANGELOG.md * Add ledger_hash, ledger_index to transaction_entry * Fix validated and close_time_iso in account_tx * Fix typos * Improve getJson for Transaction and STTx * Minor improvements * Replace class enum JsonOptions with struct We may consider turning this into a general-purpose template and using it elsewhere * simplify the extraction of transactionID from Transaction object * Remove obsolete comments * Unconditionally set validated in account_tx output * Minor improvements * Minor fixes --------- Co-authored-by: Chenna Keshava <ckeshavabs@gmail.com>
How to use RPC coroutines.
Introduction.
By default, an RPC handler runs as an uninterrupted task on the JobQueue. This is fine for commands that are fast to compute but might not be acceptable for tasks that require multiple parts or are large, like a full ledger.
For this purpose, the rippled RPC handler allows suspension with continuation
- a request to suspend execution of the RPC response and to continue it after some function or job has been executed. A default continuation is supplied which simply reschedules the job on the JobQueue, or the programmer can supply their own.
The classes.
Suspension with continuation uses four std::functions in the ripple::RPC
namespace:
using Callback = std::function <void ()>;
using Continuation = std::function <void (Callback const&)>;
using Suspend = std::function <void (Continuation const&)>;
using Coroutine = std::function <void (Suspend const&)>;
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
would prevent any other task from making forward progress when you call a
Callback.
A Continuation is a function that is given a Callback and promises to call
it later. A Continuation guarantees to call the Callback exactly once at
some point in the future, but it does not have to be immediately or even in the
current thread.
A Suspend is a function belonging to a Coroutine. A Suspend runs a
Continuation, passing it a Callback that continues execution of the
Coroutine.
And finally, a Coroutine is a std::function which is given a
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.
The flow of control.
Given these functions, the flow of RPC control when using coroutines is straight-forward.
-
The instance of
ServerHandlerreceives an RPC request. -
It creates a
Coroutineand gives it to the coroutine manager. -
The coroutine manager creates a
Coroutine, starts it up, and then calls theCoroutinewith aSuspend. -
Now the RPC response starts to be calculated.
-
When the RPC handler wants to suspend, it calls the
Suspendfunction with aContinuation. -
Coroutine execution is suspended.
-
The
Continuationis called with aCallbackthat the coroutine manager creates. -
The
Continuationmay choose to execute immediately, defer execution on the job queue, or wait for some resource to be free. -
When the
Continuationis finished, it calls theCallbackthat the coroutine manager gave it, perhaps a long time ago. -
This
Callbackcontinues execution on the suspendedCoroutinefrom where it left off.