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:
Scott Schurr
2017-02-24 16:40:23 -08:00
committed by Brad Chase
parent 1bb92d40aa
commit c453df927f
7 changed files with 391 additions and 43 deletions

View 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);
}

View File

@@ -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>