rippled
Loading...
Searching...
No Matches
JobQueue.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 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#ifndef RIPPLE_CORE_JOBQUEUE_H_INCLUDED
21#define RIPPLE_CORE_JOBQUEUE_H_INCLUDED
22
23#include <xrpld/core/ClosureCounter.h>
24#include <xrpld/core/JobTypeData.h>
25#include <xrpld/core/JobTypes.h>
26#include <xrpld/core/detail/Workers.h>
27
28#include <xrpl/basics/LocalValue.h>
29#include <xrpl/json/json_value.h>
30
31#include <boost/coroutine/all.hpp>
32
33#include <set>
34
35namespace ripple {
36
37namespace perf {
38class PerfLog;
39}
40
41class Logs;
43{
44 explicit Coro_create_t() = default;
45};
46
58{
59public:
62 {
63 private:
72 boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
73 boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
74#ifndef NDEBUG
75 bool finished_ = false;
76#endif
77
78 public:
79 // Private: Used in the implementation
80 template <class F>
82
83 // Not copy-constructible or assignable
84 Coro(Coro const&) = delete;
85 Coro&
86 operator=(Coro const&) = delete;
87
89
99 void
100 yield() const;
101
115 bool
117
127 void
129
131 bool
132 runnable() const;
133
135 void
137
139 void
141 };
142
143 using JobFunction = std::function<void()>;
144
145 JobQueue(
146 int threadCount,
147 beast::insight::Collector::ptr const& collector,
148 beast::Journal journal,
149 Logs& logs,
150 perf::PerfLog& perfLog);
151 ~JobQueue();
152
162 template <
163 typename JobHandler,
165 decltype(std::declval<JobHandler&&>()()),
166 void>::value>>
167 bool
168 addJob(JobType type, std::string const& name, JobHandler&& jobHandler)
169 {
170 if (auto optionalCountedJob =
172 {
173 return addRefCountedJob(type, name, std::move(*optionalCountedJob));
174 }
175 return false;
176 }
177
187 template <class F>
189 postCoro(JobType t, std::string const& name, F&& f);
190
193 int
194 getJobCount(JobType t) const;
195
198 int
199 getJobCountTotal(JobType t) const;
200
203 int
204 getJobCountGE(JobType t) const;
205
209 makeLoadEvent(JobType t, std::string const& name);
210
213 void
214 addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed);
215
216 // Cannot be const because LoadMonitor has no const methods.
217 bool
218 isOverloaded();
219
220 // Cannot be const because LoadMonitor has no const methods.
222 getJson(int c = 0);
223
225 void
226 rendezvous();
227
228 void
229 stop();
230
231 bool
233 {
234 return stopping_;
235 }
236
237 // We may be able to move away from this, but we can keep it during the
238 // transition.
239 bool
240 isStopped() const;
241
242private:
243 friend class Coro;
244
246
256
257 // The number of jobs currently in processTask()
259
260 // The number of suspended coroutines
261 int nSuspend_ = 0;
262
264
265 // Statistics tracking
270
272
273 void
274 collect();
277
278 // Adds a reference counted job to the JobQueue.
279 //
280 // param type The type of job.
281 // param name Name of the job.
282 // param func std::function with signature void (Job&). Called when the
283 // job is executed.
284 //
285 // return true if func added to queue.
286 bool
288 JobType type,
289 std::string const& name,
290 JobFunction const& func);
291
292 // Returns the next Job we should run now.
293 //
294 // RunnableJob:
295 // A Job in the JobSet whose slots count for its type is greater than zero.
296 //
297 // Pre-conditions:
298 // mJobSet must not be empty.
299 // mJobSet holds at least one RunnableJob
300 //
301 // Post-conditions:
302 // job is a valid Job object.
303 // job is removed from mJobQueue.
304 // Waiting job count of its type is decremented
305 // Running job count of its type is incremented
306 //
307 // Invariants:
308 // The calling thread owns the JobLock
309 void
310 getNextJob(Job& job);
311
312 // Indicates that a running Job has completed its task.
313 //
314 // Pre-conditions:
315 // Job must not exist in mJobSet.
316 // The JobType must not be invalid.
317 //
318 // Post-conditions:
319 // The running count of that JobType is decremented
320 // A new task is signaled if there are more waiting Jobs than the limit, if
321 // any.
322 //
323 // Invariants:
324 // <none>
325 void
326 finishJob(JobType type);
327
328 // Runs the next appropriate waiting Job.
329 //
330 // Pre-conditions:
331 // A RunnableJob must exist in the JobSet
332 //
333 // Post-conditions:
334 // The chosen RunnableJob will have Job::doJob() called.
335 //
336 // Invariants:
337 // <none>
338 void
339 processTask(int instance) override;
340
341 // Returns the limit of running jobs for the given job type.
342 // For jobs with no limit, we return the largest int. Hopefully that
343 // will be enough.
344 int
345 getJobLimit(JobType type);
346};
347
348/*
349 An RPC command is received and is handled via ServerHandler(HTTP) or
350 Handler(websocket), depending on the connection type. The handler then calls
351 the JobQueue::postCoro() method to create a coroutine and run it at a later
352 point. This frees up the handler thread and allows it to continue handling
353 other requests while the RPC command completes its work asynchronously.
354
355 postCoro() creates a Coro object. When the Coro ctor is called, and its
356 coro_ member is initialized (a boost::coroutines::pull_type), execution
357 automatically passes to the coroutine, which we don't want at this point,
358 since we are still in the handler thread context. It's important to note
359 here that construction of a boost pull_type automatically passes execution to
360 the coroutine. A pull_type object automatically generates a push_type that is
361 passed as a parameter (do_yield) in the signature of the function the
362 pull_type was created with. This function is immediately called during coro_
363 construction and within it, Coro::yield_ is assigned the push_type
364 parameter (do_yield) address and called (yield()) so we can return execution
365 back to the caller's stack.
366
367 postCoro() then calls Coro::post(), which schedules a job on the job
368 queue to continue execution of the coroutine in a JobQueue worker thread at
369 some later time. When the job runs, we lock on the Coro::mutex_ and call
370 coro_ which continues where we had left off. Since we the last thing we did
371 in coro_ was call yield(), the next thing we continue with is calling the
372 function param f, that was passed into Coro ctor. It is within this
373 function body that the caller specifies what he would like to do while
374 running in the coroutine and allow them to suspend and resume execution.
375 A task that relies on other events to complete, such as path finding, calls
376 Coro::yield() to suspend its execution while waiting on those events to
377 complete and continue when signaled via the Coro::post() method.
378
379 There is a potential race condition that exists here where post() can get
380 called before yield() after f is called. Technically the problem only occurs
381 if the job that post() scheduled is executed before yield() is called.
382 If the post() job were to be executed before yield(), undefined behavior
383 would occur. The lock ensures that coro_ is not called again until we exit
384 the coroutine. At which point a scheduled resume() job waiting on the lock
385 would gain entry, harmlessly call coro_ and immediately return as we have
386 already completed the coroutine.
387
388 The race condition occurs as follows:
389
390 1- The coroutine is running.
391 2- The coroutine is about to suspend, but before it can do so, it must
392 arrange for some event to wake it up.
393 3- The coroutine arranges for some event to wake it up.
394 4- Before the coroutine can suspend, that event occurs and the
395 resumption of the coroutine is scheduled on the job queue. 5- Again, before
396 the coroutine can suspend, the resumption of the coroutine is dispatched. 6-
397 Again, before the coroutine can suspend, the resumption code runs the
398 coroutine.
399 The coroutine is now running in two threads.
400
401 The lock prevents this from happening as step 6 will block until the
402 lock is released which only happens after the coroutine completes.
403*/
404
405} // namespace ripple
406
407#include <xrpld/core/Coro.ipp>
408
409namespace ripple {
410
411template <class F>
414{
415 /* First param is a detail type to make construction private.
416 Last param is the function the coroutine runs. Signature of
417 void(std::shared_ptr<Coro>).
418 */
419 auto coro = std::make_shared<Coro>(
420 Coro_create_t{}, *this, t, name, std::forward<F>(f));
421 if (!coro->post())
422 {
423 // The Coro was not successfully posted. Disable it so it's destructor
424 // can run with no negative side effects. Then destroy it.
425 coro->expectEarlyExit();
426 coro.reset();
427 }
428 return coro;
429}
430
431} // namespace ripple
432
433#endif
Represents a JSON value.
Definition json_value.h:149
A generic endpoint for log messages.
Definition Journal.h:60
A metric for measuring an integral value.
Definition Gauge.h:40
A reference to a handler for performing polled collection.
Definition Hook.h:32
std::optional< Substitute< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
Coroutines must run to completion.
Definition JobQueue.h:62
void join()
Waits until coroutine returns from the user function.
std::string name_
Definition JobQueue.h:67
std::mutex mutex_run_
Definition JobQueue.h:70
void resume()
Resume coroutine execution.
std::condition_variable cv_
Definition JobQueue.h:71
void expectEarlyExit()
Once called, the Coro allows early exit without an assert.
bool runnable() const
Returns true if the Coro is still runnable (has not returned).
bool post()
Schedule coroutine execution.
detail::LocalValues lvs_
Definition JobQueue.h:64
Coro & operator=(Coro const &)=delete
Coro(Coro_create_t, JobQueue &, JobType, std::string const &, F &&)
boost::coroutines::asymmetric_coroutine< void >::push_type * yield_
Definition JobQueue.h:73
void yield() const
Suspend coroutine execution.
boost::coroutines::asymmetric_coroutine< void >::pull_type coro_
Definition JobQueue.h:72
Coro(Coro const &)=delete
A pool of threads to perform work.
Definition JobQueue.h:58
JobTypeData m_invalidJobData
Definition JobQueue.h:255
int getJobLimit(JobType type)
Definition JobQueue.cpp:441
std::shared_ptr< Coro > postCoro(JobType t, std::string const &name, F &&f)
Creates a coroutine and adds a job to the queue which will run it.
Definition JobQueue.h:413
std::atomic_bool stopped_
Definition JobQueue.h:253
std::uint64_t m_lastJob
Definition JobQueue.h:249
JobDataMap m_jobData
Definition JobQueue.h:254
void rendezvous()
Block until no jobs running.
Definition JobQueue.cpp:273
JobCounter jobCounter_
Definition JobQueue.h:251
bool isStopping() const
Definition JobQueue.h:232
int getJobCountTotal(JobType t) const
Jobs waiting plus running at this priority.
Definition JobQueue.cpp:152
bool isStopped() const
Definition JobQueue.cpp:322
perf::PerfLog & perfLog_
Definition JobQueue.h:266
Workers m_workers
Definition JobQueue.h:263
JobTypeData & getJobTypeData(JobType type)
Definition JobQueue.cpp:280
int getJobCountGE(JobType t) const
All waiting jobs at or greater than this priority.
Definition JobQueue.cpp:162
void addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed)
Add multiple load events.
Definition JobQueue.cpp:193
Json::Value getJson(int c=0)
Definition JobQueue.cpp:214
beast::insight::Collector::ptr m_collector
Definition JobQueue.h:267
bool addRefCountedJob(JobType type, std::string const &name, JobFunction const &func)
Definition JobQueue.cpp:82
std::unique_ptr< LoadEvent > makeLoadEvent(JobType t, std::string const &name)
Return a scoped LoadEvent.
Definition JobQueue.cpp:179
std::set< Job > m_jobSet
Definition JobQueue.h:250
int getJobCount(JobType t) const
Jobs waiting at this priority.
Definition JobQueue.cpp:142
beast::insight::Hook hook
Definition JobQueue.h:269
beast::Journal m_journal
Definition JobQueue.h:247
std::mutex m_mutex
Definition JobQueue.h:248
beast::insight::Gauge job_count
Definition JobQueue.h:268
void processTask(int instance) override
Perform a task.
Definition JobQueue.cpp:388
std::atomic_bool stopping_
Definition JobQueue.h:252
void finishJob(JobType type)
Definition JobQueue.cpp:365
void getNextJob(Job &job)
Definition JobQueue.cpp:328
std::condition_variable cv_
Definition JobQueue.h:271
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition JobQueue.h:168
Manages partitions for logging.
Definition Log.h:52
Workers is effectively a thread pool.
Definition Workers.h:82
Singleton class that maintains performance counters and optionally writes Json-formatted data to a di...
Definition PerfLog.h:52
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
JobType
Definition Job.h:35
T reset(T... args)
Called to perform tasks as needed.
Definition Workers.h:86