mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 03:26:01 +00:00
Prevent misuse of JobQueue header files:
* Move `JobCoro` to `JobQueue::Coro` and remove separate JobCoro.h
This commit is contained in:
committed by
Nik Bougalis
parent
afd4b45036
commit
4b261b12a4
@@ -20,26 +20,31 @@
|
||||
#ifndef RIPPLE_CORE_JOBQUEUE_H_INCLUDED
|
||||
#define RIPPLE_CORE_JOBQUEUE_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/LocalValue.h>
|
||||
#include <ripple/basics/win32_workaround.h>
|
||||
#include <ripple/core/Job.h>
|
||||
#include <ripple/core/JobTypes.h>
|
||||
#include <ripple/core/JobTypeData.h>
|
||||
#include <ripple/core/JobCoro.h>
|
||||
#include <ripple/core/impl/Workers.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/beast/insight/Collector.h>
|
||||
#include <ripple/core/Stoppable.h>
|
||||
#include <boost/coroutine/all.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Logs;
|
||||
struct Coro_create_t {};
|
||||
|
||||
/** A pool of threads to perform work.
|
||||
|
||||
A job posted will always run to completion.
|
||||
|
||||
|
||||
Coroutines that are suspended must be resumed,
|
||||
and run to completion.
|
||||
|
||||
@@ -51,6 +56,61 @@ class JobQueue
|
||||
, private Workers::Callback
|
||||
{
|
||||
public:
|
||||
/** Coroutines must run to completion. */
|
||||
class Coro : public std::enable_shared_from_this<Coro>
|
||||
{
|
||||
private:
|
||||
detail::LocalValues lvs_;
|
||||
JobQueue& jq_;
|
||||
JobType type_;
|
||||
std::string name_;
|
||||
bool running_;
|
||||
std::mutex mutex_;
|
||||
std::mutex mutex_run_;
|
||||
std::condition_variable cv_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
|
||||
#ifndef NDEBUG
|
||||
bool finished_ = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Private: Used in the implementation
|
||||
template <class F>
|
||||
Coro(Coro_create_t, JobQueue&, JobType,
|
||||
std::string const&, F&&);
|
||||
|
||||
// Not copy-constructible or assignable
|
||||
Coro(Coro const&) = delete;
|
||||
Coro& operator= (Coro const&) = delete;
|
||||
|
||||
~Coro();
|
||||
|
||||
/** Suspend coroutine execution.
|
||||
Effects:
|
||||
The coroutine's stack is saved.
|
||||
The associated Job thread is released.
|
||||
Note:
|
||||
The associated Job function returns.
|
||||
Undefined behavior if called consecutively without a corresponding post.
|
||||
*/
|
||||
void yield() const;
|
||||
|
||||
/** Schedule coroutine execution.
|
||||
Effects:
|
||||
Returns immediately.
|
||||
A new job is scheduled to resume the execution of the coroutine.
|
||||
When the job runs, the coroutine's stack is restored and execution
|
||||
continues at the beginning of coroutine function or the statement
|
||||
after the previous call to yield.
|
||||
Undefined behavior if called consecutively without a corresponding yield.
|
||||
*/
|
||||
void post();
|
||||
|
||||
/** Waits until coroutine returns from the user function. */
|
||||
void join();
|
||||
};
|
||||
|
||||
using JobFunction = std::function <void(Job&)>;
|
||||
|
||||
JobQueue (beast::insight::Collector::ptr const& collector,
|
||||
@@ -63,7 +123,7 @@ public:
|
||||
|
||||
@param t The type of job.
|
||||
@param name Name of the job.
|
||||
@param f Has a signature of void(std::shared_ptr<JobCoro>). Called when the job executes.
|
||||
@param f Has a signature of void(std::shared_ptr<Coro>). Called when the job executes.
|
||||
*/
|
||||
template <class F>
|
||||
void postCoro (JobType t, std::string const& name, F&& f);
|
||||
@@ -111,7 +171,7 @@ public:
|
||||
rendezvous();
|
||||
|
||||
private:
|
||||
friend class JobCoro;
|
||||
friend class Coro;
|
||||
|
||||
using JobDataMap = std::map <JobType, JobTypeData>;
|
||||
|
||||
@@ -235,7 +295,7 @@ private:
|
||||
point. This frees up the handler thread and allows it to continue handling
|
||||
other requests while the RPC command completes its work asynchronously.
|
||||
|
||||
postCoro() creates a JobCoro object. When the JobCoro ctor is called, and its
|
||||
postCoro() creates a Coro object. When the Coro ctor is called, and its
|
||||
coro_ member is initialized(a boost::coroutines::pull_type), execution
|
||||
automatically passes to the coroutine, which we don't want at this point,
|
||||
since we are still in the handler thread context. It's important to note here
|
||||
@@ -243,21 +303,21 @@ private:
|
||||
coroutine. A pull_type object automatically generates a push_type that is
|
||||
used as the as a parameter(do_yield) in the signature of the function the
|
||||
pull_type was created with. This function is immediately called during coro_
|
||||
construction and within it, JobCoro::yield_ is assigned the push_type
|
||||
construction and within it, Coro::yield_ is assigned the push_type
|
||||
parameter(do_yield) address and called(yield()) so we can return execution
|
||||
back to the caller's stack.
|
||||
|
||||
postCoro() then calls JobCoro::post(), which schedules a job on the job
|
||||
postCoro() then calls Coro::post(), which schedules a job on the job
|
||||
queue to continue execution of the coroutine in a JobQueue worker thread at
|
||||
some later time. When the job runs, we lock on the JobCoro::mutex_ and call
|
||||
some later time. When the job runs, we lock on the Coro::mutex_ and call
|
||||
coro_ which continues where we had left off. Since we the last thing we did
|
||||
in coro_ was call yield(), the next thing we continue with is calling the
|
||||
function param f, that was passed into JobCoro ctor. It is within this
|
||||
function param f, that was passed into Coro ctor. It is within this
|
||||
function body that the caller specifies what he would like to do while
|
||||
running in the coroutine and allow them to suspend and resume execution.
|
||||
A task that relies on other events to complete, such as path finding, calls
|
||||
JobCoro::yield() to suspend its execution while waiting on those events to
|
||||
complete and continue when signaled via the JobCoro::post() method.
|
||||
Coro::yield() to suspend its execution while waiting on those events to
|
||||
complete and continue when signaled via the Coro::post() method.
|
||||
|
||||
There is a potential race condition that exists here where post() can get
|
||||
called before yield() after f is called. Technically the problem only occurs
|
||||
@@ -288,7 +348,7 @@ private:
|
||||
|
||||
} // ripple
|
||||
|
||||
#include <ripple/core/JobCoro.ipp>
|
||||
#include <ripple/core/Coro.ipp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -297,10 +357,10 @@ void JobQueue::postCoro (JobType t, std::string const& name, F&& f)
|
||||
{
|
||||
/* First param is a detail type to make construction private.
|
||||
Last param is the function the coroutine runs. Signature of
|
||||
void(std::shared_ptr<JobCoro>).
|
||||
void(std::shared_ptr<Coro>).
|
||||
*/
|
||||
auto const coro = std::make_shared<JobCoro>(
|
||||
detail::JobCoro_create_t{}, *this, t, name, std::forward<F>(f));
|
||||
auto const coro = std::make_shared<Coro>(
|
||||
Coro_create_t{}, *this, t, name, std::forward<F>(f));
|
||||
coro->post();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user