Files
rippled/src/ripple/app/main/LoadManager.cpp
Donovan Hide 7eaca149c1 Remove boost_thread dependency (RIPD-216).
Fixes RIPD-216
2014-10-15 19:37:25 -04:00

256 lines
7.5 KiB
C++

//------------------------------------------------------------------------------
/*
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/LoadFeeTrack.h>
namespace ripple {
class LoadManagerImp
: public LoadManager
, public beast::Thread
{
public:
/* Entry mapping utilization to cost.
The cost is expressed as a unitless relative quantity. These
mappings are statically loaded at startup with heuristic values.
*/
class Cost
{
public:
// VFALCO TODO Eliminate this default ctor somehow
Cost ()
: m_loadType ()
, m_cost (0)
, m_resourceFlags (0)
{
}
Cost (LoadType loadType, int cost, int resourceFlags)
: m_loadType (loadType)
, m_cost (cost)
, m_resourceFlags (resourceFlags)
{
}
LoadType getLoadType () const
{
return m_loadType;
}
int getCost () const
{
return m_cost;
}
int getResourceFlags () const
{
return m_resourceFlags;
}
public:
// VFALCO TODO Make these private and const
LoadType m_loadType;
int m_cost;
int m_resourceFlags;
};
//--------------------------------------------------------------------------
beast::Journal m_journal;
typedef RippleMutex LockType;
typedef std::lock_guard <LockType> ScopedLockType;
LockType mLock;
bool mArmed;
int mDeadLock; // Detect server deadlocks
std::vector <Cost> mCosts;
//--------------------------------------------------------------------------
LoadManagerImp (Stoppable& parent, beast::Journal journal)
: LoadManager (parent)
, Thread ("loadmgr")
, m_journal (journal)
, mArmed (false)
, mDeadLock (0)
, mCosts (LT_MAX)
{
UptimeTimer::getInstance ().beginManualUpdates ();
}
~LoadManagerImp ()
{
UptimeTimer::getInstance ().endManualUpdates ();
stopThread ();
}
//--------------------------------------------------------------------------
//
// Stoppable
//
//--------------------------------------------------------------------------
void onPrepare ()
{
}
void onStart ()
{
m_journal.debug << "Starting";
startThread ();
}
void onStop ()
{
if (isThreadRunning ())
{
m_journal.debug << "Stopping";
stopThreadAsync ();
}
else
{
stopped ();
}
}
//--------------------------------------------------------------------------
void resetDeadlockDetector ()
{
ScopedLockType sl (mLock);
mDeadLock = UptimeTimer::getInstance ().getElapsedSeconds ();
}
void activateDeadlockDetector ()
{
mArmed = true;
}
void logDeadlock (int dlTime)
{
m_journal.warning << "Server stalled for " << dlTime << " seconds.";
}
// VFALCO NOTE Where's the thread object? It's not a data member...
//
void run ()
{
using clock_type = std::chrono::steady_clock;
// Initialize the clock to the current time.
auto t = clock_type::now();
while (! threadShouldExit ())
{
{
// VFALCO NOTE What is this lock protecting?
ScopedLockType sl (mLock);
// VFALCO NOTE I think this is to reduce calls to the operating system
// for retrieving the current time.
//
// TODO Instead of incrementing can't we just retrieve the current
// time again?
//
// Manually update the timer.
UptimeTimer::getInstance ().incrementElapsedTime ();
// Measure the amount of time we have been deadlocked, in seconds.
//
// VFALCO NOTE mDeadLock is a canary for detecting the condition.
int const timeSpentDeadlocked = UptimeTimer::getInstance ().getElapsedSeconds () - mDeadLock;
// VFALCO NOTE I think that "armed" refers to the deadlock detector
//
int const reportingIntervalSeconds = 10;
if (mArmed && (timeSpentDeadlocked >= reportingIntervalSeconds))
{
// Report the deadlocked condition every 10 seconds
if ((timeSpentDeadlocked % reportingIntervalSeconds) == 0)
{
logDeadlock (timeSpentDeadlocked);
}
// If we go over 500 seconds spent deadlocked, it means that the
// deadlock resolution code has failed, which qualifies as undefined
// behavior.
//
assert (timeSpentDeadlocked < 500);
}
}
bool change;
// VFALCO TODO Eliminate the dependence on the Application object.
// Choices include constructing with the job queue / feetracker.
// Another option is using an observer pattern to invert the dependency.
if (getApp().getJobQueue ().isOverloaded ())
{
m_journal.info << getApp().getJobQueue ().getJson (0);
change = getApp().getFeeTrack ().raiseLocalFee ();
}
else
{
change = getApp().getFeeTrack ().lowerLocalFee ();
}
if (change)
{
// VFALCO TODO replace this with a Listener / observer and subscribe in NetworkOPs or Application
getApp().getOPs ().reportFeeChange ();
}
t += std::chrono::seconds (1);
auto const duration = t - clock_type::now();
if ((duration < std::chrono::seconds (0)) || (duration > std::chrono::seconds (1)))
{
m_journal.warning << "time jump";
t = clock_type::now();
}
else
{
std::this_thread::sleep_for (duration);
}
}
stopped ();
}
};
//------------------------------------------------------------------------------
LoadManager::LoadManager (Stoppable& parent)
: Stoppable ("LoadManager", parent)
{
}
//------------------------------------------------------------------------------
LoadManager* LoadManager::New (Stoppable& parent, beast::Journal journal)
{
return new LoadManagerImp (parent, journal);
}
} // ripple