mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Remove unused or obsolete classes and files
This commit is contained in:
@@ -27,9 +27,9 @@
|
||||
#include <ripple/protocol/SystemParameters.h>
|
||||
#include <ripple/net/HTTPClient.h>
|
||||
#include <beast/http/URL.h>
|
||||
#include <beast/module/core/text/LexicalCast.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <beast/streams/debug_ostream.h>
|
||||
#include <beast/utility/ci_char_traits.h>
|
||||
#include <beast/ci_char_traits.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
259
src/ripple/core/impl/DeadlineTimer.cpp
Normal file
259
src/ripple/core/impl/DeadlineTimer.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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/DeadlineTimer.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class DeadlineTimer::Manager
|
||||
: protected beast::Thread
|
||||
{
|
||||
private:
|
||||
using Items = beast::List <DeadlineTimer>;
|
||||
|
||||
public:
|
||||
Manager () : beast::Thread ("DeadlineTimer::Manager")
|
||||
{
|
||||
startThread ();
|
||||
}
|
||||
|
||||
~Manager ()
|
||||
{
|
||||
signalThreadShouldExit ();
|
||||
notify ();
|
||||
waitForThreadToExit ();
|
||||
assert (m_items.empty ());
|
||||
}
|
||||
|
||||
static
|
||||
Manager&
|
||||
instance()
|
||||
{
|
||||
static Manager m;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Okay to call on an active timer.
|
||||
// However, an extra notification may still happen due to concurrency.
|
||||
//
|
||||
void activate (DeadlineTimer& timer,
|
||||
double secondsRecurring, beast::RelativeTime const& when)
|
||||
{
|
||||
assert (secondsRecurring >= 0);
|
||||
|
||||
std::lock_guard <std::recursive_mutex> lock (m_mutex);
|
||||
|
||||
if (timer.m_isActive)
|
||||
{
|
||||
m_items.erase (m_items.iterator_to (timer));
|
||||
|
||||
timer.m_isActive = false;
|
||||
}
|
||||
|
||||
timer.m_secondsRecurring = secondsRecurring;
|
||||
timer.m_notificationTime = when;
|
||||
|
||||
insertSorted (timer);
|
||||
timer.m_isActive = true;
|
||||
|
||||
notify ();
|
||||
}
|
||||
|
||||
// Okay to call this on an inactive timer.
|
||||
// This can happen naturally based on concurrency.
|
||||
//
|
||||
void deactivate (DeadlineTimer& timer)
|
||||
{
|
||||
std::lock_guard <std::recursive_mutex> lock (m_mutex);
|
||||
|
||||
if (timer.m_isActive)
|
||||
{
|
||||
m_items.erase (m_items.iterator_to (timer));
|
||||
|
||||
timer.m_isActive = false;
|
||||
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
while (! threadShouldExit ())
|
||||
{
|
||||
beast::RelativeTime const currentTime (
|
||||
beast::RelativeTime::fromStartup ());
|
||||
|
||||
double seconds (0);
|
||||
DeadlineTimer* timer (nullptr);
|
||||
|
||||
{
|
||||
std::lock_guard <std::recursive_mutex> lock (m_mutex);
|
||||
|
||||
// See if a timer expired
|
||||
if (! m_items.empty ())
|
||||
{
|
||||
timer = &m_items.front ();
|
||||
|
||||
// Has this timer expired?
|
||||
if (timer->m_notificationTime <= currentTime)
|
||||
{
|
||||
// Expired, remove it from the list.
|
||||
assert (timer->m_isActive);
|
||||
m_items.pop_front ();
|
||||
|
||||
// Is the timer recurring?
|
||||
if (timer->m_secondsRecurring > 0)
|
||||
{
|
||||
// Yes so set the timer again.
|
||||
timer->m_notificationTime =
|
||||
currentTime + timer->m_secondsRecurring;
|
||||
|
||||
// Put it back into the list as active
|
||||
insertSorted (*timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a recurring timer, deactivate it.
|
||||
timer->m_isActive = false;
|
||||
}
|
||||
|
||||
timer->m_listener->onDeadlineTimer (*timer);
|
||||
|
||||
// re-loop
|
||||
seconds = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
seconds = (
|
||||
timer->m_notificationTime - currentTime).inSeconds ();
|
||||
|
||||
// Can't be zero and come into the else clause.
|
||||
assert (seconds != 0);
|
||||
|
||||
// Don't call the listener
|
||||
timer = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we have released the lock here.
|
||||
|
||||
if (seconds > 0)
|
||||
{
|
||||
// Wait until interrupt or next timer.
|
||||
//
|
||||
int const milliSeconds (std::max (
|
||||
static_cast <int> (seconds * 1000 + 0.5), 1));
|
||||
assert (milliSeconds > 0);
|
||||
wait (milliSeconds);
|
||||
}
|
||||
else if (seconds == 0)
|
||||
{
|
||||
// Wait until interrupt
|
||||
//
|
||||
wait ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not wait. This can happen if the recurring timer duration
|
||||
// is extremely short, or if a listener wastes too much time in
|
||||
// their callback.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Caller is responsible for locking
|
||||
void insertSorted (DeadlineTimer& timer)
|
||||
{
|
||||
if (! m_items.empty ())
|
||||
{
|
||||
Items::iterator before = m_items.begin ();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (before->m_notificationTime >= timer.m_notificationTime)
|
||||
{
|
||||
m_items.insert (before, timer);
|
||||
break;
|
||||
}
|
||||
|
||||
++before;
|
||||
|
||||
if (before == m_items.end ())
|
||||
{
|
||||
m_items.push_back (timer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.push_back (timer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex m_mutex;
|
||||
Items m_items;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DeadlineTimer::DeadlineTimer (Listener* listener)
|
||||
: m_listener (listener)
|
||||
, m_isActive (false)
|
||||
{
|
||||
}
|
||||
|
||||
DeadlineTimer::~DeadlineTimer ()
|
||||
{
|
||||
Manager::instance().deactivate (*this);
|
||||
}
|
||||
|
||||
void DeadlineTimer::cancel ()
|
||||
{
|
||||
Manager::instance().deactivate (*this);
|
||||
}
|
||||
|
||||
void DeadlineTimer::setExpiration (double secondsUntilDeadline)
|
||||
{
|
||||
assert (secondsUntilDeadline != 0);
|
||||
|
||||
beast::RelativeTime const when (
|
||||
beast::RelativeTime::fromStartup() + secondsUntilDeadline);
|
||||
|
||||
Manager::instance().activate (*this, 0, when);
|
||||
}
|
||||
|
||||
void DeadlineTimer::setRecurringExpiration (double secondsUntilDeadline)
|
||||
{
|
||||
assert (secondsUntilDeadline != 0);
|
||||
|
||||
beast::RelativeTime const when (
|
||||
beast::RelativeTime::fromStartup() + secondsUntilDeadline);
|
||||
|
||||
Manager::instance().activate (*this, secondsUntilDeadline, when);
|
||||
}
|
||||
|
||||
} // beast
|
||||
@@ -22,8 +22,7 @@
|
||||
#include <ripple/core/JobTypes.h>
|
||||
#include <ripple/core/JobTypeInfo.h>
|
||||
#include <ripple/core/JobTypeData.h>
|
||||
#include <beast/chrono/chrono_util.h>
|
||||
#include <beast/module/core/thread/Workers.h>
|
||||
#include <beast/clock/chrono_util.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <ripple/basics/random.h>
|
||||
#include <ripple/core/impl/SNTPClock.h>
|
||||
#include <beast/asio/placeholders.h>
|
||||
#include <beast/threads/Thread.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cmath>
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#ifndef RIPPLE_NET_SNTPCLOCK_H_INCLUDED
|
||||
#define RIPPLE_NET_SNTPCLOCK_H_INCLUDED
|
||||
|
||||
#include <beast/chrono/abstract_clock.h>
|
||||
#include <beast/utility/Journal.h>
|
||||
#include <beast/clock/abstract_clock.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
215
src/ripple/core/impl/Stoppable.cpp
Normal file
215
src/ripple/core/impl/Stoppable.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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 <ripple/core/Stoppable.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
Stoppable::Stoppable (char const* name, RootStoppable& root)
|
||||
: m_name (name)
|
||||
, m_root (root)
|
||||
, m_child (this)
|
||||
, m_started (false)
|
||||
, m_stopped (false)
|
||||
, m_childrenStopped (false)
|
||||
{
|
||||
}
|
||||
|
||||
Stoppable::Stoppable (char const* name, Stoppable& parent)
|
||||
: m_name (name)
|
||||
, m_root (parent.m_root)
|
||||
, m_child (this)
|
||||
, m_started (false)
|
||||
, m_stopped (false)
|
||||
, m_childrenStopped (false)
|
||||
{
|
||||
// Must not have stopping parent.
|
||||
assert (! parent.isStopping());
|
||||
|
||||
parent.m_children.push_front (&m_child);
|
||||
}
|
||||
|
||||
Stoppable::~Stoppable ()
|
||||
{
|
||||
// Children must be stopped.
|
||||
assert (!m_started || m_childrenStopped);
|
||||
}
|
||||
|
||||
bool Stoppable::isStopping() const
|
||||
{
|
||||
return m_root.isStopping();
|
||||
}
|
||||
|
||||
bool Stoppable::isStopped () const
|
||||
{
|
||||
return m_stopped;
|
||||
}
|
||||
|
||||
bool Stoppable::areChildrenStopped () const
|
||||
{
|
||||
return m_childrenStopped;
|
||||
}
|
||||
|
||||
void Stoppable::stopped ()
|
||||
{
|
||||
m_stoppedEvent.signal();
|
||||
}
|
||||
|
||||
void Stoppable::onPrepare ()
|
||||
{
|
||||
}
|
||||
|
||||
void Stoppable::onStart ()
|
||||
{
|
||||
}
|
||||
|
||||
void Stoppable::onStop ()
|
||||
{
|
||||
stopped();
|
||||
}
|
||||
|
||||
void Stoppable::onChildrenStopped ()
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Stoppable::prepareRecursive ()
|
||||
{
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->prepareRecursive ();
|
||||
onPrepare ();
|
||||
}
|
||||
|
||||
void Stoppable::startRecursive ()
|
||||
{
|
||||
onStart ();
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->startRecursive ();
|
||||
}
|
||||
|
||||
void Stoppable::stopAsyncRecursive (beast::Journal j)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto const start = high_resolution_clock::now();
|
||||
onStop ();
|
||||
auto const ms = duration_cast<milliseconds>(
|
||||
high_resolution_clock::now() - start).count();
|
||||
|
||||
#ifdef NDEBUG
|
||||
if (ms >= 10)
|
||||
if (auto stream = j.fatal())
|
||||
stream << m_name << "::onStop took " << ms << "ms";
|
||||
#else
|
||||
(void)ms;
|
||||
#endif
|
||||
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->stopAsyncRecursive(j);
|
||||
}
|
||||
|
||||
void Stoppable::stopRecursive (beast::Journal j)
|
||||
{
|
||||
// Block on each child from the bottom of the tree up.
|
||||
//
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->stopRecursive (j);
|
||||
|
||||
// if we get here then all children have stopped
|
||||
//
|
||||
m_childrenStopped = true;
|
||||
onChildrenStopped ();
|
||||
|
||||
// Now block on this Stoppable.
|
||||
//
|
||||
bool const timedOut (! m_stoppedEvent.wait (1 * 1000)); // milliseconds
|
||||
if (timedOut)
|
||||
{
|
||||
if (auto stream = j.error())
|
||||
stream << "Waiting for '" << m_name << "' to stop";
|
||||
m_stoppedEvent.wait ();
|
||||
}
|
||||
|
||||
// once we get here, we know the stoppable has stopped.
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
RootStoppable::RootStoppable (char const* name)
|
||||
: Stoppable (name, *this)
|
||||
, m_prepared (false)
|
||||
, m_calledStop (false)
|
||||
, m_calledStopAsync (false)
|
||||
{
|
||||
}
|
||||
|
||||
bool RootStoppable::isStopping() const
|
||||
{
|
||||
return m_calledStopAsync;
|
||||
}
|
||||
|
||||
void RootStoppable::prepare ()
|
||||
{
|
||||
if (m_prepared.exchange (true) == false)
|
||||
prepareRecursive ();
|
||||
}
|
||||
|
||||
void RootStoppable::start ()
|
||||
{
|
||||
// Courtesy call to prepare.
|
||||
if (m_prepared.exchange (true) == false)
|
||||
prepareRecursive ();
|
||||
|
||||
if (m_started.exchange (true) == false)
|
||||
startRecursive ();
|
||||
}
|
||||
|
||||
void RootStoppable::stop (beast::Journal j)
|
||||
{
|
||||
// Must have a prior call to start()
|
||||
assert (m_started);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
if (m_calledStop)
|
||||
{
|
||||
if (auto stream = j.warn())
|
||||
stream << "Stoppable::stop called again";
|
||||
return;
|
||||
}
|
||||
m_calledStop = true;
|
||||
c_.notify_all();
|
||||
}
|
||||
stopAsync (j);
|
||||
stopRecursive (j);
|
||||
}
|
||||
|
||||
void RootStoppable::stopAsync(beast::Journal j)
|
||||
{
|
||||
if (m_calledStopAsync.exchange (true) == false)
|
||||
stopAsyncRecursive(j);
|
||||
}
|
||||
|
||||
}
|
||||
294
src/ripple/core/impl/Workers.cpp
Normal file
294
src/ripple/core/impl/Workers.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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 <ripple/core/impl/Workers.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
Workers::Workers (
|
||||
Callback& callback,
|
||||
std::string const& threadNames,
|
||||
int numberOfThreads)
|
||||
: m_callback (callback)
|
||||
, m_threadNames (threadNames)
|
||||
, m_allPaused (true, true)
|
||||
, m_semaphore (0)
|
||||
, m_numberOfThreads (0)
|
||||
, m_activeCount (0)
|
||||
, m_pauseCount (0)
|
||||
, m_runningTaskCount (0)
|
||||
{
|
||||
setNumberOfThreads (numberOfThreads);
|
||||
}
|
||||
|
||||
Workers::~Workers ()
|
||||
{
|
||||
pauseAllThreadsAndWait ();
|
||||
|
||||
deleteWorkers (m_everyone);
|
||||
}
|
||||
|
||||
int Workers::getNumberOfThreads () const noexcept
|
||||
{
|
||||
return m_numberOfThreads;
|
||||
}
|
||||
|
||||
// VFALCO NOTE if this function is called quickly to reduce then
|
||||
// increase the number of threads, it could result in
|
||||
// more paused threads being created than expected.
|
||||
//
|
||||
void Workers::setNumberOfThreads (int numberOfThreads)
|
||||
{
|
||||
if (m_numberOfThreads != numberOfThreads)
|
||||
{
|
||||
if (numberOfThreads > m_numberOfThreads)
|
||||
{
|
||||
// Increasing the number of working threads
|
||||
|
||||
int const amount = numberOfThreads - m_numberOfThreads;
|
||||
|
||||
for (int i = 0; i < amount; ++i)
|
||||
{
|
||||
// See if we can reuse a paused worker
|
||||
Worker* worker = m_paused.pop_front ();
|
||||
|
||||
if (worker != nullptr)
|
||||
{
|
||||
// If we got here then the worker thread is at [1]
|
||||
// This will unblock their call to wait()
|
||||
//
|
||||
worker->notify ();
|
||||
}
|
||||
else
|
||||
{
|
||||
worker = new Worker (*this, m_threadNames);
|
||||
}
|
||||
|
||||
m_everyone.push_front (worker);
|
||||
}
|
||||
}
|
||||
else if (numberOfThreads < m_numberOfThreads)
|
||||
{
|
||||
// Decreasing the number of working threads
|
||||
|
||||
int const amount = m_numberOfThreads - numberOfThreads;
|
||||
|
||||
for (int i = 0; i < amount; ++i)
|
||||
{
|
||||
++m_pauseCount;
|
||||
|
||||
// Pausing a thread counts as one "internal task"
|
||||
m_semaphore.signal ();
|
||||
}
|
||||
}
|
||||
|
||||
m_numberOfThreads = numberOfThreads;
|
||||
}
|
||||
}
|
||||
|
||||
void Workers::pauseAllThreadsAndWait ()
|
||||
{
|
||||
setNumberOfThreads (0);
|
||||
|
||||
m_allPaused.wait ();
|
||||
|
||||
assert (numberOfCurrentlyRunningTasks () == 0);
|
||||
}
|
||||
|
||||
void Workers::addTask ()
|
||||
{
|
||||
m_semaphore.signal ();
|
||||
}
|
||||
|
||||
int Workers::numberOfCurrentlyRunningTasks () const noexcept
|
||||
{
|
||||
return m_runningTaskCount.load ();
|
||||
}
|
||||
|
||||
void Workers::deleteWorkers (beast::LockFreeStack <Worker>& stack)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
Worker* const worker = stack.pop_front ();
|
||||
|
||||
if (worker != nullptr)
|
||||
{
|
||||
// This call blocks until the thread orderly exits
|
||||
delete worker;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Workers::Worker::Worker (Workers& workers, std::string const& threadName)
|
||||
: Thread (threadName)
|
||||
, m_workers (workers)
|
||||
{
|
||||
startThread ();
|
||||
}
|
||||
|
||||
Workers::Worker::~Worker ()
|
||||
{
|
||||
stopThread ();
|
||||
}
|
||||
|
||||
void Workers::Worker::run ()
|
||||
{
|
||||
while (! threadShouldExit ())
|
||||
{
|
||||
// Increment the count of active workers, and if
|
||||
// we are the first one then reset the "all paused" event
|
||||
//
|
||||
if (++m_workers.m_activeCount == 1)
|
||||
m_workers.m_allPaused.reset ();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Acquire a task or "internal task."
|
||||
//
|
||||
m_workers.m_semaphore.wait ();
|
||||
|
||||
// See if there's a pause request. This
|
||||
// counts as an "internal task."
|
||||
//
|
||||
int pauseCount = m_workers.m_pauseCount.load ();
|
||||
|
||||
if (pauseCount > 0)
|
||||
{
|
||||
// Try to decrement
|
||||
pauseCount = --m_workers.m_pauseCount;
|
||||
|
||||
if (pauseCount >= 0)
|
||||
{
|
||||
// We got paused
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Undo our decrement
|
||||
++m_workers.m_pauseCount;
|
||||
}
|
||||
}
|
||||
|
||||
// We couldn't pause so we must have gotten
|
||||
// unblocked in order to process a task.
|
||||
//
|
||||
++m_workers.m_runningTaskCount;
|
||||
m_workers.m_callback.processTask ();
|
||||
--m_workers.m_runningTaskCount;
|
||||
|
||||
// Put the name back in case the callback changed it
|
||||
Thread::setCurrentThreadName (Thread::getThreadName());
|
||||
}
|
||||
|
||||
// Any worker that goes into the paused list must
|
||||
// guarantee that it will eventually block on its
|
||||
// event object.
|
||||
//
|
||||
m_workers.m_paused.push_front (this);
|
||||
|
||||
// Decrement the count of active workers, and if we
|
||||
// are the last one then signal the "all paused" event.
|
||||
//
|
||||
if (--m_workers.m_activeCount == 0)
|
||||
m_workers.m_allPaused.signal ();
|
||||
|
||||
Thread::setCurrentThreadName ("(" + getThreadName() + ")");
|
||||
|
||||
// [1] We will be here when the paused list is popped
|
||||
//
|
||||
// We block on our event object, a requirement of being
|
||||
// put into the paused list.
|
||||
//
|
||||
// This will get signaled on either a reactivate or a stopThread()
|
||||
//
|
||||
wait ();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Workers_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
struct TestCallback : Workers::Callback
|
||||
{
|
||||
explicit TestCallback (int count_)
|
||||
: finished (false, count_ == 0)
|
||||
, count (count_)
|
||||
{
|
||||
}
|
||||
|
||||
void processTask ()
|
||||
{
|
||||
if (--count == 0)
|
||||
finished.signal ();
|
||||
}
|
||||
|
||||
beast::WaitableEvent finished;
|
||||
std::atomic <int> count;
|
||||
};
|
||||
|
||||
void testThreads (int const threadCount)
|
||||
{
|
||||
testcase ("threadCount = " + std::to_string (threadCount));
|
||||
|
||||
TestCallback cb (threadCount);
|
||||
|
||||
Workers w (cb, "Test", 0);
|
||||
expect (w.getNumberOfThreads () == 0);
|
||||
|
||||
w.setNumberOfThreads (threadCount);
|
||||
expect (w.getNumberOfThreads () == threadCount);
|
||||
|
||||
for (int i = 0; i < threadCount; ++i)
|
||||
w.addTask ();
|
||||
|
||||
// 10 seconds should be enough to finish on any system
|
||||
//
|
||||
bool signaled = cb.finished.wait (10 * 1000);
|
||||
expect (signaled, "timed out");
|
||||
|
||||
w.pauseAllThreadsAndWait ();
|
||||
|
||||
// We had better finished all our work!
|
||||
expect (cb.count.load () == 0, "Did not complete task!");
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
testThreads (0);
|
||||
testThreads (1);
|
||||
testThreads (2);
|
||||
testThreads (4);
|
||||
testThreads (16);
|
||||
testThreads (64);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Workers,core,ripple);
|
||||
|
||||
} // beast
|
||||
154
src/ripple/core/impl/Workers.h
Normal file
154
src/ripple/core/impl/Workers.h
Normal file
@@ -0,0 +1,154 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_WORKERS_H_INCLUDED
|
||||
#define RIPPLE_CORE_WORKERS_H_INCLUDED
|
||||
|
||||
#include <ripple/core/impl/semaphore.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <ripple/beast/core/LockFreeStack.h>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** A group of threads that process tasks.
|
||||
*/
|
||||
class Workers
|
||||
{
|
||||
public:
|
||||
/** Called to perform tasks as needed. */
|
||||
struct Callback
|
||||
{
|
||||
/** Perform a task.
|
||||
|
||||
The call is made on a thread owned by Workers. It is important
|
||||
that you only process one task from inside your callback. Each
|
||||
call to addTask will result in exactly one call to processTask.
|
||||
|
||||
@see Workers::addTask
|
||||
*/
|
||||
virtual void processTask () = 0;
|
||||
};
|
||||
|
||||
/** Create the object.
|
||||
|
||||
A number of initial threads may be optionally specified. The
|
||||
default is to create one thread per CPU.
|
||||
|
||||
@param threadNames The name given to each created worker thread.
|
||||
*/
|
||||
explicit Workers (Callback& callback,
|
||||
std::string const& threadNames = "Worker",
|
||||
int numberOfThreads =
|
||||
static_cast<int>(std::thread::hardware_concurrency()));
|
||||
|
||||
~Workers ();
|
||||
|
||||
/** Retrieve the desired number of threads.
|
||||
|
||||
This just returns the number of active threads that were requested. If
|
||||
there was a recent call to setNumberOfThreads, the actual number of active
|
||||
threads may be temporarily different from what was last requested.
|
||||
|
||||
@note This function is not thread-safe.
|
||||
*/
|
||||
int getNumberOfThreads () const noexcept;
|
||||
|
||||
/** Set the desired number of threads.
|
||||
@note This function is not thread-safe.
|
||||
*/
|
||||
void setNumberOfThreads (int numberOfThreads);
|
||||
|
||||
/** Pause all threads and wait until they are paused.
|
||||
|
||||
If a thread is processing a task it will pause as soon as the task
|
||||
completes. There may still be tasks signaled even after all threads
|
||||
have paused.
|
||||
|
||||
@note This function is not thread-safe.
|
||||
*/
|
||||
void pauseAllThreadsAndWait ();
|
||||
|
||||
/** Add a task to be performed.
|
||||
|
||||
Every call to addTask will eventually result in a call to
|
||||
Callback::processTask unless the Workers object is destroyed or
|
||||
the number of threads is never set above zero.
|
||||
|
||||
@note This function is thread-safe.
|
||||
*/
|
||||
void addTask ();
|
||||
|
||||
/** Get the number of currently executing calls of Callback::processTask.
|
||||
While this function is thread-safe, the value may not stay
|
||||
accurate for very long. It's mainly for diagnostic purposes.
|
||||
*/
|
||||
int numberOfCurrentlyRunningTasks () const noexcept;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
struct PausedTag { };
|
||||
|
||||
/* A Worker executes tasks on its provided thread.
|
||||
|
||||
These are the states:
|
||||
|
||||
Active: Running the task processing loop.
|
||||
Idle: Active, but blocked on waiting for a task.
|
||||
Pausd: Blocked waiting to exit or become active.
|
||||
*/
|
||||
class Worker
|
||||
: public beast::LockFreeStack <Worker>::Node
|
||||
, public beast::LockFreeStack <Worker, PausedTag>::Node
|
||||
, public beast::Thread
|
||||
{
|
||||
public:
|
||||
Worker (Workers& workers, std::string const& threadName);
|
||||
|
||||
~Worker ();
|
||||
|
||||
private:
|
||||
void run ();
|
||||
|
||||
private:
|
||||
Workers& m_workers;
|
||||
};
|
||||
|
||||
private:
|
||||
static void deleteWorkers (beast::LockFreeStack <Worker>& stack);
|
||||
|
||||
private:
|
||||
Callback& m_callback;
|
||||
std::string m_threadNames; // The name to give each thread
|
||||
beast::WaitableEvent m_allPaused; // signaled when all threads paused
|
||||
semaphore m_semaphore; // each pending task is 1 resource
|
||||
int m_numberOfThreads; // how many we want active now
|
||||
std::atomic <int> m_activeCount; // to know when all are paused
|
||||
std::atomic <int> m_pauseCount; // how many threads need to pause now
|
||||
std::atomic <int> m_runningTaskCount; // how many calls to processTask() active
|
||||
beast::LockFreeStack <Worker> m_everyone; // holds all created workers
|
||||
beast::LockFreeStack <Worker, PausedTag> m_paused; // holds just paused workers
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
87
src/ripple/core/impl/semaphore.h
Normal file
87
src/ripple/core/impl/semaphore.h
Normal file
@@ -0,0 +1,87 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_SEMAPHORE_H_INCLUDED
|
||||
#define RIPPLE_CORE_SEMAPHORE_H_INCLUDED
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
template <class Mutex, class CondVar>
|
||||
class basic_semaphore
|
||||
{
|
||||
private:
|
||||
using scoped_lock = std::unique_lock <Mutex>;
|
||||
|
||||
Mutex m_mutex;
|
||||
CondVar m_cond;
|
||||
std::size_t m_count;
|
||||
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
|
||||
/** Create the semaphore, with an optional initial count.
|
||||
If unspecified, the initial count is zero.
|
||||
*/
|
||||
explicit basic_semaphore (size_type count = 0)
|
||||
: m_count (count)
|
||||
{
|
||||
}
|
||||
|
||||
/** Increment the count and unblock one waiting thread. */
|
||||
void notify ()
|
||||
{
|
||||
scoped_lock lock (m_mutex);
|
||||
++m_count;
|
||||
m_cond.notify_one ();
|
||||
}
|
||||
|
||||
// Deprecated, for backward compatibility
|
||||
void signal () { notify (); }
|
||||
|
||||
/** Block until notify is called. */
|
||||
void wait ()
|
||||
{
|
||||
scoped_lock lock (m_mutex);
|
||||
while (m_count == 0)
|
||||
m_cond.wait (lock);
|
||||
--m_count;
|
||||
}
|
||||
|
||||
/** Perform a non-blocking wait.
|
||||
@return `true` If the wait would be satisfied.
|
||||
*/
|
||||
bool try_wait ()
|
||||
{
|
||||
scoped_lock lock (m_mutex);
|
||||
if (m_count == 0)
|
||||
return false;
|
||||
--m_count;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using semaphore = basic_semaphore <std::mutex, std::condition_variable>;
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user