mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
NetworkOPs isn't stopped() until Jobs done (RIPD-1356):
A new JobCounter class is introduced. The JobCounter keeps a reference count of Jobs in flight to the JobQueue. When NetworkOPs needs to stop, in addition to other work, it calls JobCounter::join(), which waits until all Jobs in flight have been destroyed before returning. This ensures that all NetworkOPs Jobs are completed before NetworkOPs declares itself stopped(). Also, once a JobCounter is join()ed, it refuses to produce more counted Jobs for the JobQueue. So, once all old Jobs in flight are done, then NetworkOPs will add no additional Jobs to the JobQueue. Other classes besides NetworkOPs should also be able to use JobCounter. NetworkOPs is a first test case. Also unneeded #includes were removed from files touched for other reasons.
This commit is contained in:
122
src/test/core/JobCounter_test.cpp
Normal file
122
src/test/core/JobCounter_test.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2017 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/core/JobCounter.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class JobCounter_test : public beast::unit_test::suite
|
||||
{
|
||||
void testWrap()
|
||||
{
|
||||
// Verify reference counting.
|
||||
JobCounter jobCounter;
|
||||
BEAST_EXPECT (jobCounter.count() == 0);
|
||||
{
|
||||
auto wrapped1 = jobCounter.wrap ([] (Job&) {});
|
||||
BEAST_EXPECT (jobCounter.count() == 1);
|
||||
|
||||
// wrapped1 should be callable with a Job.
|
||||
{
|
||||
Job j;
|
||||
(*wrapped1)(j);
|
||||
}
|
||||
{
|
||||
// Copy should increase reference count.
|
||||
auto wrapped2 (wrapped1);
|
||||
BEAST_EXPECT (jobCounter.count() == 2);
|
||||
{
|
||||
// Move should increase reference count.
|
||||
auto wrapped3 (std::move(wrapped2));
|
||||
BEAST_EXPECT (jobCounter.count() == 3);
|
||||
{
|
||||
// An additional Job also increases count.
|
||||
auto wrapped4 = jobCounter.wrap ([] (Job&) {});
|
||||
BEAST_EXPECT (jobCounter.count() == 4);
|
||||
}
|
||||
BEAST_EXPECT (jobCounter.count() == 3);
|
||||
}
|
||||
BEAST_EXPECT (jobCounter.count() == 2);
|
||||
}
|
||||
BEAST_EXPECT (jobCounter.count() == 1);
|
||||
}
|
||||
BEAST_EXPECT (jobCounter.count() == 0);
|
||||
|
||||
// Join with 0 count should not stall.
|
||||
jobCounter.join();
|
||||
|
||||
// Wrapping a Job after join() should return boost::none.
|
||||
BEAST_EXPECT (jobCounter.wrap ([] (Job&) {}) == boost::none);
|
||||
}
|
||||
|
||||
void testWaitOnJoin()
|
||||
{
|
||||
// Verify reference counting.
|
||||
JobCounter jobCounter;
|
||||
BEAST_EXPECT (jobCounter.count() == 0);
|
||||
|
||||
auto job = (jobCounter.wrap ([] (Job&) {}));
|
||||
BEAST_EXPECT (jobCounter.count() == 1);
|
||||
|
||||
// Calling join() now should stall, so do it on a different thread.
|
||||
std::atomic<bool> threadExited {false};
|
||||
std::thread localThread ([&jobCounter, &threadExited] ()
|
||||
{
|
||||
// Should stall after calling join.
|
||||
jobCounter.join();
|
||||
threadExited.store (true);
|
||||
});
|
||||
|
||||
// Wait for the thread to call jobCounter.join().
|
||||
while (! jobCounter.joined());
|
||||
|
||||
// The thread should still be active after waiting a millisecond.
|
||||
// This is not a guarantee that join() stalled the thread, but it
|
||||
// improves confidence.
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for (1ms);
|
||||
BEAST_EXPECT (threadExited == false);
|
||||
|
||||
// Destroy the Job and expect the thread to exit (asynchronously).
|
||||
job = boost::none;
|
||||
BEAST_EXPECT (jobCounter.count() == 0);
|
||||
|
||||
// Wait for the thread to exit.
|
||||
while (threadExited == false);
|
||||
localThread.join();
|
||||
}
|
||||
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
testWrap();
|
||||
testWaitOnJoin();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(JobCounter, core, ripple);
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <test/core/Config_test.cpp>
|
||||
#include <test/core/Coroutine_test.cpp>
|
||||
#include <test/core/DeadlineTimer_test.cpp>
|
||||
#include <test/core/JobCounter_test.cpp>
|
||||
#include <test/core/SociDB_test.cpp>
|
||||
#include <test/core/Stoppable_test.cpp>
|
||||
#include <test/core/TerminateHandler_test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user