Replace UptimeTimer with UptimeClock

* UptimeClock is a chrono-compatible seconds-precision clock.

* Like UptimeTimer, its purpose is to make it possible for clients
  to query the uptime thousands of times per second without a
  significant performance hit.

* UptimeClock decouples itself from LoadManager by managing its
  own once-per-second update loop.

* Clients now traffic in chrono time_points and durations instead
  of int.
This commit is contained in:
Howard Hinnant
2018-04-23 13:46:37 -04:00
committed by seelabs
parent 717f874767
commit 7d163a45dc
14 changed files with 159 additions and 186 deletions

View File

@@ -238,7 +238,7 @@ public:
std::weak_ptr<Peer> const& wPeer,
std::shared_ptr<protocol::TMGetObjectByHash> const& request,
uint256 haveLedgerHash,
std::uint32_t uUptime);
UptimeClock::time_point uptime);
std::size_t getFetchPackCacheSize () const;

View File

@@ -36,7 +36,7 @@
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/TaggedCache.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/nodestore/DatabaseShard.h>
#include <ripple/overlay/Overlay.h>
@@ -1792,9 +1792,9 @@ LedgerMaster::makeFetchPack (
std::weak_ptr<Peer> const& wPeer,
std::shared_ptr<protocol::TMGetObjectByHash> const& request,
uint256 haveLedgerHash,
std::uint32_t uUptime)
UptimeClock::time_point uptime)
{
if (UptimeTimer::getInstance ().getElapsedSeconds () > (uUptime + 1))
if (UptimeClock::now() > uptime + 1s)
{
JLOG(m_journal.info()) << "Fetch pack request got stale";
return;
@@ -1916,7 +1916,7 @@ LedgerMaster::makeFetchPack (
wantLedger = getLedgerByHash (haveLedger->info().parentHash);
}
while (wantLedger &&
UptimeTimer::getInstance ().getElapsedSeconds () <= uUptime + 1);
UptimeClock::now() <= uptime + 1s);
JLOG(m_journal.info())
<< "Built fetch pack with " << reply.objects ().size () << " nodes";

View File

@@ -21,7 +21,7 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/json/to_string.h>
#include <ripple/beast/core/CurrentThreadName.h>
#include <memory>
@@ -35,18 +35,16 @@ LoadManager::LoadManager (
: Stoppable ("LoadManager", parent)
, app_ (app)
, journal_ (journal)
, deadLock_ (0)
, deadLock_ ()
, armed_ (false)
, stop_ (false)
{
UptimeTimer::getInstance ().beginManualUpdates ();
}
LoadManager::~LoadManager ()
{
try
{
UptimeTimer::getInstance ().endManualUpdates ();
onStop ();
}
catch (std::exception const& ex)
@@ -67,9 +65,7 @@ void LoadManager::activateDeadlockDetector ()
void LoadManager::resetDeadlockDetector ()
{
auto const elapsedSeconds =
UptimeTimer::getInstance ().getElapsedSeconds ();
auto const elapsedSeconds = UptimeClock::now();
std::lock_guard<std::mutex> sl (mutex_);
deadLock_ = elapsedSeconds;
}
@@ -108,24 +104,14 @@ void LoadManager::run ()
{
beast::setCurrentThreadName ("LoadManager");
using clock_type = std::chrono::steady_clock;
using clock_type = std::chrono::system_clock;
// Initialize the clock to the current time.
auto t = clock_type::now();
bool stop = false;
while (! (stop || isStopping ()))
{
{
// 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 ();
// Copy out shared data under a lock. Use copies outside lock.
std::unique_lock<std::mutex> sl (mutex_);
auto const deadLock = deadLock_;
@@ -134,29 +120,24 @@ void LoadManager::run ()
sl.unlock();
// Measure the amount of time we have been deadlocked, in seconds.
//
// VFALCO NOTE deadLock_ is a canary for detecting the condition.
int const timeSpentDeadlocked =
UptimeTimer::getInstance ().getElapsedSeconds () - deadLock;
auto const timeSpentDeadlocked = UptimeClock::now() - deadLock;
// VFALCO NOTE I think that "armed" refers to the deadlock detector.
//
int const reportingIntervalSeconds = 10;
auto const reportingIntervalSeconds = 10s;
if (armed && (timeSpentDeadlocked >= reportingIntervalSeconds))
{
// Report the deadlocked condition every 10 seconds
if ((timeSpentDeadlocked % reportingIntervalSeconds) == 0)
if ((timeSpentDeadlocked % reportingIntervalSeconds) == 0s)
{
JLOG(journal_.warn())
<< "Server stalled for "
<< timeSpentDeadlocked << " seconds.";
<< timeSpentDeadlocked.count() << " seconds.";
}
// 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);
assert (timeSpentDeadlocked < 500s);
}
}
@@ -178,18 +159,17 @@ void LoadManager::run ()
app_.getOPs ().reportFeeChange ();
}
t += std::chrono::seconds (1);
t += 1s;
auto const duration = t - clock_type::now();
if ((duration < std::chrono::seconds (0)) ||
(duration > std::chrono::seconds (1)))
if ((duration < 0s) || (duration > 1s))
{
JLOG(journal_.warn()) << "time jump";
t = clock_type::now();
}
else
{
alertable_sleep_for(duration);
alertable_sleep_until(t);
}
}
@@ -202,7 +182,7 @@ std::unique_ptr<LoadManager>
make_LoadManager (Application& app,
Stoppable& parent, beast::Journal journal)
{
return std::make_unique<LoadManager>(app, parent, journal);
return std::unique_ptr<LoadManager>{new LoadManager{app, parent, journal}};
}
} // ripple

View File

@@ -42,16 +42,6 @@ class Application;
*/
class LoadManager : public Stoppable
{
public:
// It would be better if the LoadManager constructor could be private
// with std::make_unique as a friend. But Visual Studio can't currently
// swallow the following friend declaration (Microsoft (R) C/C++
// Optimizing Compiler Version 19.00.23026 for x64). So we make the
// constructor public.
// template<class T, class... Args>
// friend std::unique_ptr<T> std::make_unique (Args&&... args);
// Should only be constructible by std::make_unique.
LoadManager (Application& app, Stoppable& parent, beast::Journal journal);
public:
@@ -105,9 +95,13 @@ private:
std::thread thread_;
std::mutex mutex_; // Guards deadLock_, armed_, and stop_.
int deadLock_; // Detect server deadlocks.
UptimeClock::time_point deadLock_; // Detect server deadlocks.
bool armed_;
bool stop_;
friend
std::unique_ptr<LoadManager>
make_LoadManager(Application& app, Stoppable& parent, beast::Journal journal);
};
std::unique_ptr<LoadManager>

View File

@@ -41,7 +41,7 @@
#include <ripple/app/tx/apply.h>
#include <ripple/basics/mulDiv.h>
#include <ripple/basics/PerfLog.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/crypto/csprng.h>
#include <ripple/crypto/RFC1751.h>
@@ -2335,7 +2335,7 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin, bool counters)
}
info[jss::state_accounting] = accounting_.json();
info[jss::uptime] = UptimeTimer::getInstance ().getElapsedSeconds ();
info[jss::uptime] = UptimeClock::now().time_since_epoch().count();
info[jss::jq_trans_overflow] = std::to_string(
app_.overlay().getJqTransOverflow());
info[jss::peer_disconnects] = std::to_string(

View File

@@ -20,39 +20,47 @@
#ifndef RIPPLE_BASICS_UPTIMETIMER_H_INCLUDED
#define RIPPLE_BASICS_UPTIMETIMER_H_INCLUDED
#include <ctime>
#include <atomic>
#include <chrono>
#include <ratio>
#include <thread>
namespace ripple {
/** Tracks program uptime.
/** Tracks program uptime to seconds precision.
The timer can be switched to a manual system of updating, to reduce
system calls. (?)
The timer caches the current time as a performance optimization.
This allows clients to query the current time thousands of times
per second.
*/
// VFALCO TODO determine if the non-manual timing is actually needed
class UptimeTimer
class UptimeClock
{
private:
UptimeTimer ();
~UptimeTimer ();
public:
int getElapsedSeconds () const;
using rep = int;
using period = std::ratio<1>;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<UptimeClock>;
static constexpr bool is_steady = std::chrono::system_clock::is_steady;
void beginManualUpdates ();
void endManualUpdates ();
explicit UptimeClock() = default;
void incrementElapsedTime ();
static UptimeTimer& getInstance ();
static time_point now(); // seconds since rippled program start
private:
// VFALCO DEPRECATED, Use a memory barrier instead of forcing a cache line
int volatile m_elapsedTime;
static std::atomic<rep> now_;
static std::atomic<bool> stop_;
time_t m_startTime;
struct update_thread
: private std::thread
{
~update_thread();
update_thread(update_thread&&) = default;
bool m_isUpdatingManually;
using std::thread::thread;
};
static update_thread start_clock();
};
} // ripple

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
/*
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/basics/UptimeClock.h>
namespace ripple {
std::atomic<UptimeClock::rep> UptimeClock::now_{0}; // seconds since start
std::atomic<bool> UptimeClock::stop_{false}; // stop update thread
// On rippled shutdown, cancel and wait for the update thread
UptimeClock::update_thread::~update_thread()
{
if (joinable())
{
stop_ = true;
// This join() may take up to a 1s, but happens only
// once at rippled shutdown.
join();
}
}
// Launch the update thread
UptimeClock::update_thread
UptimeClock::start_clock()
{
return update_thread{[]
{
using namespace std;
using namespace std::chrono;
// Wake up every second and update now_
auto next = system_clock::now() + 1s;
while (!stop_)
{
this_thread::sleep_until(next);
next += 1s;
++now_;
}
}};
}
// This actually measures time since first use, instead of since rippled start.
// However the difference between these two epochs is a small fraction of a second
// and unimportant.
UptimeClock::time_point
UptimeClock::now()
{
// start the update thread on first use
static const auto init = start_clock();
// Return the number of seconds since rippled start
return time_point{duration{now_}};
}
} // ripple

View File

@@ -1,82 +0,0 @@
//------------------------------------------------------------------------------
/*
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/basics/UptimeTimer.h>
#include <atomic>
namespace ripple {
UptimeTimer::UptimeTimer ()
: m_elapsedTime (0)
, m_startTime (::time (0))
, m_isUpdatingManually (false)
{
}
UptimeTimer::~UptimeTimer ()
{
}
int UptimeTimer::getElapsedSeconds () const
{
int result;
if (m_isUpdatingManually)
{
std::atomic_thread_fence (std::memory_order_seq_cst);
result = m_elapsedTime;
}
else
{
// VFALCO TODO use time_t instead of int return
result = static_cast <int> (::time (0) - m_startTime);
}
return result;
}
void UptimeTimer::beginManualUpdates ()
{
//assert (!m_isUpdatingManually);
m_isUpdatingManually = true;
}
void UptimeTimer::endManualUpdates ()
{
//assert (m_isUpdatingManually);
m_isUpdatingManually = false;
}
void UptimeTimer::incrementElapsedTime ()
{
//assert (m_isUpdatingManually);
++m_elapsedTime;
}
UptimeTimer& UptimeTimer::getInstance ()
{
static UptimeTimer instance;
return instance;
}
} // ripple

View File

@@ -20,6 +20,7 @@
#ifndef RIPPLE_CORE_LOADMONITOR_H_INCLUDED
#define RIPPLE_CORE_LOADMONITOR_H_INCLUDED
#include <ripple/basics/UptimeClock.h>
#include <ripple/core/LoadEvent.h>
#include <ripple/beast/utility/Journal.h>
#include <chrono>
@@ -73,7 +74,7 @@ private:
std::chrono::milliseconds mLatencyMSPeak;
std::chrono::milliseconds mTargetLatencyAvg;
std::chrono::milliseconds mTargetLatencyPk;
int mLastUpdate;
UptimeClock::time_point mLastUpdate;
beast::Journal j_;
};

View File

@@ -226,10 +226,9 @@ public:
@return `true` if we are stopping
*/
template <class Rep, class Period>
bool
alertable_sleep_for(
std::chrono::duration<Rep, Period> const& d);
alertable_sleep_until(
std::chrono::system_clock::time_point const& t);
protected:
/** Called by derived classes to indicate that the stoppable has stopped. */
@@ -373,10 +372,9 @@ public:
@return `true` if we are stopping
*/
template <class Rep, class Period>
bool
alertable_sleep_for(
std::chrono::duration<Rep, Period> const& d);
alertable_sleep_until(
std::chrono::system_clock::time_point const& t);
private:
/* Notify a root stoppable and children to stop, without waiting.
@@ -407,23 +405,23 @@ JobCounter& Stoppable::jobCounter ()
//------------------------------------------------------------------------------
template <class Rep, class Period>
inline
bool
RootStoppable::alertable_sleep_for(
std::chrono::duration<Rep, Period> const& d)
RootStoppable::alertable_sleep_until(
std::chrono::system_clock::time_point const& t)
{
std::unique_lock<std::mutex> lock(m_);
if (m_calledStop)
return true;
return c_.wait_for(lock, d, [this]{return m_calledStop.load();});
return c_.wait_until(lock, t, [this]{return m_calledStop.load();});
}
template <class Rep, class Period>
inline
bool
Stoppable::alertable_sleep_for(
std::chrono::duration<Rep, Period> const& d)
Stoppable::alertable_sleep_until(
std::chrono::system_clock::time_point const& t)
{
return m_root.alertable_sleep_for(d);
return m_root.alertable_sleep_until(t);
}
} // ripple

View File

@@ -18,7 +18,7 @@
//==============================================================================
#include <ripple/basics/Log.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/basics/date.h>
#include <ripple/core/LoadMonitor.h>
@@ -52,7 +52,7 @@ LoadMonitor::LoadMonitor (beast::Journal j)
, mLatencyMSPeak (0)
, mTargetLatencyAvg (0)
, mTargetLatencyPk (0)
, mLastUpdate (UptimeTimer::getInstance ().getElapsedSeconds ())
, mLastUpdate (UptimeClock::now())
, j_ (j)
{
}
@@ -66,12 +66,12 @@ LoadMonitor::LoadMonitor (beast::Journal j)
void LoadMonitor::update ()
{
using namespace std::chrono_literals;
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
auto now = UptimeClock::now();
if (now == mLastUpdate) // current
return;
// VFALCO TODO Why 8?
if ((now < mLastUpdate) || (now > (mLastUpdate + 8)))
if ((now < mLastUpdate) || (now > (mLastUpdate + 8s)))
{
// way out of date
mCounts = 0;
@@ -93,7 +93,7 @@ void LoadMonitor::update ()
*/
do
{
++mLastUpdate;
mLastUpdate += 1s;
mCounts -= ((mCounts + 3) / 4);
mLatencyEvents -= ((mLatencyEvents + 3) / 4);
mLatencyMSAvg -= (mLatencyMSAvg / 4);

View File

@@ -30,7 +30,7 @@
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/app/tx/apply.h>
#include <ripple/basics/random.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/beast/core/LexicalCast.h>
#include <ripple/beast/core/SemanticVersion.h>
#include <ripple/nodestore/DatabaseShard.h>
@@ -1864,7 +1864,7 @@ PeerImp::doFetchPack (const std::shared_ptr<protocol::TMGetObjectByHash>& packet
memcpy (hash.begin (), packet->ledgerhash ().data (), 32);
std::weak_ptr<PeerImp> weak = shared_from_this();
auto elapsed = UptimeTimer::getInstance().getElapsedSeconds();
auto elapsed = UptimeClock::now();
auto const pap = &app_;
app_.getJobQueue ().addJob (
jtPACK, "MakeFetchPack",

View File

@@ -22,7 +22,7 @@
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/json/json_value.h>
#include <ripple/ledger/CachedSLEs.h>
@@ -36,10 +36,11 @@
namespace ripple {
static
void textTime (
std::string& text, int& seconds, const char* unitName, int unitVal)
void
textTime(std::string& text, UptimeClock::time_point& seconds,
const char* unitName, std::chrono::seconds unitVal)
{
int i = seconds / unitVal;
auto i = seconds.time_since_epoch() / unitVal;
if (i == 0)
return;
@@ -111,12 +112,12 @@ Json::Value doGetCounts (RPC::Context& context)
ret[jss::treenode_track_size] = context.app.family().treecache().getTrackSize();
std::string uptime;
int s = UptimeTimer::getInstance ().getElapsedSeconds ();
textTime (uptime, s, "year", 365 * 24 * 60 * 60);
textTime (uptime, s, "day", 24 * 60 * 60);
textTime (uptime, s, "hour", 60 * 60);
textTime (uptime, s, "minute", 60);
textTime (uptime, s, "second", 1);
auto s = UptimeClock::now();
textTime (uptime, s, "year", 365 * 24h);
textTime (uptime, s, "day", 24h);
textTime (uptime, s, "hour", 1h);
textTime (uptime, s, "minute", 1min);
textTime (uptime, s, "second", 1s);
ret[jss::uptime] = uptime;
ret[jss::node_writes] = context.app.getNodeStore().getStoreCount();

View File

@@ -30,7 +30,7 @@
#include <ripple/basics/impl/strHex.cpp>
#include <ripple/basics/impl/StringUtilities.cpp>
#include <ripple/basics/impl/Sustain.cpp>
#include <ripple/basics/impl/UptimeTimer.cpp>
#include <ripple/basics/impl/UptimeClock.cpp>
#if DOXYGEN
#include <ripple/basics/README.md>