diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj index c9436d2d84..967fac8c27 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj +++ b/Builds/VisualStudio2012/RippleD.vcxproj @@ -1394,6 +1394,12 @@ true true + + true + true + true + true + true true diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters index ca994b33b8..1c65578fb3 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters @@ -1416,6 +1416,9 @@ [2] Old Ripple\ripple_core\nodestore\backend + + [2] Old Ripple\ripple_basics\containers + diff --git a/src/ripple_app/ledger/InboundLedgers.cpp b/src/ripple_app/ledger/InboundLedgers.cpp index 8c20362d7f..193b15168a 100644 --- a/src/ripple_app/ledger/InboundLedgers.cpp +++ b/src/ripple_app/ledger/InboundLedgers.cpp @@ -31,8 +31,9 @@ public: explicit InboundLedgersImp (Stoppable& parent) : Stoppable ("InboundLedgers", parent) , mLock (this, "InboundLedger", __FILE__, __LINE__) - , mRecentFailures ("LedgerAcquireRecentFailures", 0, - kReacquireIntervalSeconds) + , mRecentFailures ("LedgerAcquireRecentFailures", + get_abstract_clock (), + 0, kReacquireIntervalSeconds) { } @@ -225,12 +226,12 @@ public: void logFailure (uint256 const& h) { - mRecentFailures.add (h); + mRecentFailures.insert (h); } bool isFailure (uint256 const& h) { - return mRecentFailures.isPresent (h, false); + return mRecentFailures.exists (h); } void doLedgerData (Job&, LedgerHash hash) diff --git a/src/ripple_app/shamap/SHAMap.h b/src/ripple_app/shamap/SHAMap.h index 05435eacdf..a765201fb0 100644 --- a/src/ripple_app/shamap/SHAMap.h +++ b/src/ripple_app/shamap/SHAMap.h @@ -238,13 +238,16 @@ public: std::list getFetchPack (SHAMap * have, bool includeLeaves, int max); void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function); + // VFALCO NOTE These static members should be moved into a + // new Application singleton class. + // // tree node cache operations static SHAMapTreeNode::pointer getCache (uint256 const& hash, SHAMapNode const& id); static void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&); static int getFullBelowSize () { - return fullBelowCache.getSize (); + return fullBelowCache.size (); } static int getTreeNodeSize () { diff --git a/src/ripple_app/shamap/SHAMapSync.cpp b/src/ripple_app/shamap/SHAMapSync.cpp index 562428062f..ebdc693985 100644 --- a/src/ripple_app/shamap/SHAMapSync.cpp +++ b/src/ripple_app/shamap/SHAMapSync.cpp @@ -21,7 +21,10 @@ static const uint256 uZero; -KeyCache SHAMap::fullBelowCache ("fullBelowCache", 524288, 240); +KeyCache SHAMap::fullBelowCache ( + "fullBelowCache", + get_abstract_clock (), + 524288, 240); void SHAMap::visitLeaves (std::function function) { @@ -149,7 +152,7 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vectorgetChildHash (branch); - if (!fullBelowCache.isPresent (childHash)) + if (! fullBelowCache.touch_if_exists (childHash)) { SHAMapNode childID = node->getChildNodeID (branch); SHAMapTreeNode* d = getNodePointerNT (childID, childHash, filter); @@ -184,7 +187,7 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vectorsetFullBelow (); if (mType == smtSTATE) - fullBelowCache.add (node->getNodeHash ()); + fullBelowCache.insert (node->getNodeHash ()); } if (stack.empty ()) @@ -394,7 +397,7 @@ SHAMapAddNode SHAMap::addKnownNode (const SHAMapNode& node, Blob const& rawNode, return SHAMapAddNode::invalid (); } - if (fullBelowCache.isPresent (iNode->getChildHash (branch))) + if (fullBelowCache.touch_if_exists (iNode->getChildHash (branch))) return SHAMapAddNode::duplicate (); SHAMapTreeNode *nextNode = getNodePointerNT (iNode->getChildNodeID (branch), iNode->getChildHash (branch), filter); diff --git a/src/ripple_basics/containers/KeyCache.cpp b/src/ripple_basics/containers/KeyCache.cpp new file mode 100644 index 0000000000..40f58c3dd9 --- /dev/null +++ b/src/ripple_basics/containers/KeyCache.cpp @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +/* + 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 "KeyCache.h" + +namespace ripple { + +class KeyCacheTests : public UnitTest +{ +public: + void runTest () + { + beginTestCase ("Insert"); + + manual_clock clock; + clock.set (0); + + typedef std::string Key; + typedef KeyCache Cache; + + // Insert an item, retrieve it, and age it so it gets purged. + { + Cache c ("test", clock, 1, 2); + + expect (c.size () == 0); + expect (c.insert ("one")); + expect (! c.insert ("one")); + expect (c.size () == 1); + expect (c.exists ("one")); + expect (c.touch_if_exists ("one")); + ++clock; + c.sweep (); + expect (c.size () == 1); + expect (c.exists ("one")); + ++clock; + c.sweep (); + expect (c.size () == 0); + expect (! c.exists ("one")); + expect (! c.touch_if_exists ("one")); + } + + // Insert two items, have one expire + { + Cache c ("test", clock, 2, 2); + + expect (c.insert ("one")); + expect (c.size () == 1); + expect (c.insert ("two")); + expect (c.size () == 2); + ++clock; + c.sweep (); + expect (c.size () == 2); + expect (c.touch_if_exists ("two")); + ++clock; + c.sweep (); + expect (c.size () == 1); + expect (c.exists ("two")); + } + + // Insert three items (1 over limit), sweep + { + Cache c ("test", clock, 2, 3); + + expect (c.insert ("one")); + ++clock; + expect (c.insert ("two")); + ++clock; + expect (c.insert ("three")); + ++clock; + expect (c.size () == 3); + c.sweep (); + expect (c.size () < 3); + } + } + + KeyCacheTests () : UnitTest ( + "KeyCache", "ripple") + { + } +}; + +static KeyCacheTests keyCacheTests; + +} diff --git a/src/ripple_basics/containers/KeyCache.h b/src/ripple_basics/containers/KeyCache.h index 839c66ed7d..8734924be9 100644 --- a/src/ripple_basics/containers/KeyCache.h +++ b/src/ripple_basics/containers/KeyCache.h @@ -20,189 +20,186 @@ #ifndef RIPPLE_KEYCACHE_H_INCLUDED #define RIPPLE_KEYCACHE_H_INCLUDED -// This tag is for helping track the locks -struct KeyCacheBase { }; +#include "beast/beast/chrono/abstract_clock.h" + +#include +#include + +namespace ripple { /** Maintains a cache of keys with no associated data. The cache has a target size and an expiration time. When cached items become older than the maximum age they are eligible for removal during a call to @ref sweep. - - @note - Timer must provide this function: - @code - static int getElapsedSeconds (); - @endcode */ -template -class KeyCache : public KeyCacheBase +// VFALCO TODO Figure out how to pass through the allocator +template < + class Key, + class Hash = std::hash , + class KeyEqual = std::equal_to , + //class Allocator = std::allocator >, + class Mutex = std::mutex +> +class KeyCache { public: - /** Provides a type for the key. - */ typedef Key key_type; + typedef abstract_clock clock_type; + +private: + struct Entry + { + explicit Entry (clock_type::time_point const& last_access_) + : last_access (last_access_) + { + } + + clock_type::time_point last_access; + }; + + typedef std::unordered_map map_type; + typedef typename map_type::iterator iterator; + typedef std::lock_guard lock_guard; + + Mutex mutable m_mutex; + map_type m_map; + clock_type& m_clock; + std::string const m_name; + unsigned int m_target_size; + clock_type::duration m_target_age; + +public: + typedef typename map_type::size_type size_type; /** Construct with the specified name. @param size The initial target size. @param age The initial expiration time. */ - KeyCache (const std::string& name, - int size = 0, - int age = 120) - : mLock (static_cast (this), String ("KeyCache") + - "('" + name + "')", __FILE__, __LINE__) - , mName (name) - , mTargetSize (size) - , mTargetAge (age) + KeyCache (std::string const& name, + clock_type& clock, size_type target_size = 0, + clock_type::rep expiration_seconds = 120) + : m_clock (clock) + , m_name (name) + , m_target_size (target_size) + , m_target_age (std::chrono::seconds (expiration_seconds)) { - assert ((size >= 0) && (age > 2)); + assert (m_target_size >= 0); } - /** Returns the current size. - */ - unsigned int getSize () + //-------------------------------------------------------------------------- + + /** Retrieve the name of this object. */ + std::string const& name () const { - ScopedLockType sl (mLock, __FILE__, __LINE__); - return mCache.size (); + return m_name; } - /** Returns the desired target size. - */ - unsigned int getTargetSize () + /** Returns the number of items in the container. */ + size_type size () const { - ScopedLockType sl (mLock, __FILE__, __LINE__); - return mTargetSize; + lock_guard lock (m_mutex); + return m_map.size (); } - /** Returns the desired target age. - */ - unsigned int getTargetAge () + /** Empty the cache */ + void clear () { - ScopedLockType sl (mLock, __FILE__, __LINE__); - return mTargetAge; + lock_guard lock (m_mutex); + m_map.clear (); } - /** Simultaneously set the target size and age. - - @param size The target size. - @param age The target age. + /** Returns `true` if the key was found. + Does not update the last access time. */ - void setTargets (int size, int age) + template + bool exists (KeyComparable const& key) const { - ScopedLockType sl (mLock, __FILE__, __LINE__); - mTargetSize = size; - mTargetAge = age; - assert ((mTargetSize >= 0) && (mTargetAge > 2)); + lock_guard lock (m_mutex); + typename map_type::const_iterator const iter (m_map.find (key)); + return iter != m_map.end (); } - /** Retrieve the name of this object. + /** Insert the specified key. + The last access time is refreshed in all cases. + @return `true` If the key was newly inserted. */ - std::string const& getName () + bool insert (Key const& key) { - return mName; - } - - /** Determine if the specified key is cached, and optionally refresh it. - - @param key The key to check - @param refresh Whether or not to refresh the entry. - @return `true` if the key was found. - */ - bool isPresent (const key_type& key, bool refresh = true) - { - ScopedLockType sl (mLock, __FILE__, __LINE__); - - map_iterator it = mCache.find (key); - - if (it == mCache.end ()) + lock_guard lock (m_mutex); + clock_type::time_point const now (m_clock.now ()); + std::pair result (m_map.emplace ( + std::piecewise_construct, std::make_tuple (key), + std::make_tuple (now))); + if (! result.second) + { + result.first->second.last_access = now; return false; + } + return true; + } - if (refresh) - it->second = Timer::getElapsedSeconds (); - + /** Refresh the last access time on a key if present. + @return `true` If the key was found. + */ + template + bool touch_if_exists (KeyComparable const& key) + { + lock_guard lock (m_mutex); + iterator const iter (m_map.find (key)); + if (iter == m_map.end ()) + return false; + iter->second.last_access = m_clock.now (); return true; } /** Remove the specified cache entry. - @param key The key to remove. - @return `false` if the key was not found. + @return `false` If the key was not found. */ - bool del (const key_type& key) + bool erase (key_type const& key) { - ScopedLockType sl (mLock, __FILE__, __LINE__); - - map_iterator it = mCache.find (key); - - if (it == mCache.end ()) - return false; - - mCache.erase (it); - return true; + lock_guard lock (m_mutex); + return m_map.erase (key) > 0; } - /** Add the specified cache entry. - - @param key The key to add. - @return `true` if the key did not previously exist. - */ - bool add (const key_type& key) - { - ScopedLockType sl (mLock, __FILE__, __LINE__); - - map_iterator it = mCache.find (key); - - if (it != mCache.end ()) - { - it->second = Timer::getElapsedSeconds (); - return false; - } - - mCache.insert (std::make_pair (key, Timer::getElapsedSeconds ())); - return true; - } - - /** Empty the cache - */ - void clear () - { - ScopedLockType sl (mLock, __FILE__, __LINE__); - mCache.clear (); - } - - /** Remove stale entries from the cache. - */ + /** Remove stale entries from the cache. */ void sweep () { - int now = Timer::getElapsedSeconds (); - ScopedLockType sl (mLock, __FILE__, __LINE__); + clock_type::time_point const now (m_clock.now ()); + clock_type::time_point when_expire; - int target; + lock_guard lock (m_mutex); - if ((mTargetSize == 0) || (mCache.size () <= mTargetSize)) - target = now - mTargetAge; + if (m_target_size == 0 || + (m_map.size () <= m_target_size)) + { + when_expire = now - m_target_age; + } else { - target = now - (mTargetAge * mTargetSize / mCache.size ()); + when_expire = now - clock_type::duration ( + m_target_age.count() * m_target_size / m_map.size ()); - if (target > (now - 2)) - target = now - 2; + clock_type::duration const minimumAge ( + std::chrono::seconds (1)); + if (when_expire > (now - minimumAge)) + when_expire = now - minimumAge; } - map_iterator it = mCache.begin (); + iterator it = m_map.begin (); - while (it != mCache.end ()) + while (it != m_map.end ()) { - if (it->second > now) + if (it->second.last_access > now) { - it->second = now; + it->second.last_access = now; ++it; } - else if (it->second < target) + else if (it->second.last_access <= when_expire) { - it = mCache.erase (it); + it = m_map.erase (it); } else { @@ -210,21 +207,8 @@ public: } } } - -protected: - /** Provides a type for the underlying map. */ - typedef boost::unordered_map map_type; - /** The type of the iterator used for traversals. */ - typedef typename map_type::iterator map_iterator; - - typedef RippleMutex LockType; - typedef LockType::ScopedLockType ScopedLockType; - LockType mLock; - - std::string const mName; - - map_type mCache; - unsigned int mTargetSize, mTargetAge; }; +} + #endif diff --git a/src/ripple_basics/containers/TaggedCache.cpp b/src/ripple_basics/containers/TaggedCache.cpp index 1a3953d36b..19308bcfc2 100644 --- a/src/ripple_basics/containers/TaggedCache.cpp +++ b/src/ripple_basics/containers/TaggedCache.cpp @@ -32,11 +32,6 @@ original object. class TaggedCacheTests : public UnitTest { public: - TaggedCacheTests () : UnitTest ( - "TaggedCache", "ripple") - { - } - void runTest () { //Journal const j (journal()); @@ -147,6 +142,11 @@ public: expect (c.getTrackSize() == 0); } } + + TaggedCacheTests () : UnitTest ( + "TaggedCache", "ripple") + { + } }; static TaggedCacheTests taggedCacheTests; diff --git a/src/ripple_basics/containers/TaggedCache.h b/src/ripple_basics/containers/TaggedCache.h index 0a55b47fce..3d88c3aa0e 100644 --- a/src/ripple_basics/containers/TaggedCache.h +++ b/src/ripple_basics/containers/TaggedCache.h @@ -40,12 +40,13 @@ struct TaggedCacheLog; @note Callers must not modify data objects that are stored in the cache unless they hold their own lock over all cache operations. */ +// VFALCO TODO Figure out how to pass through the allocator template < class Key, class T, class Hash = std::hash , class KeyEqual = std::equal_to , - //class Allocator = std::allocator >, + //class Allocator = std::allocator >, class Mutex = std::recursive_mutex > class TaggedCacheType @@ -153,11 +154,11 @@ public: std::vector stuffToSweep; { - lock_guard lock (m_mutex); - clock_type::time_point const now (m_clock.now()); clock_type::time_point when_expire; + lock_guard lock (m_mutex); + if (m_target_size == 0 || (static_cast (m_cache.size ()) <= m_target_size)) { @@ -169,7 +170,7 @@ public: m_target_age.count() * m_target_size / m_cache.size ()); clock_type::duration const minimumAge ( - std::chrono::seconds (2)); + std::chrono::seconds (1)); if (when_expire > (now - minimumAge)) when_expire = now - minimumAge; diff --git a/src/ripple_basics/ripple_basics.cpp b/src/ripple_basics/ripple_basics.cpp index 4b8f08e812..d887dad2f2 100644 --- a/src/ripple_basics/ripple_basics.cpp +++ b/src/ripple_basics/ripple_basics.cpp @@ -45,6 +45,7 @@ namespace ripple { +#include "containers/KeyCache.cpp" #include "containers/RangeSet.cpp" #include "containers/TaggedCache.cpp" diff --git a/src/ripple_basics/ripple_basics.h b/src/ripple_basics/ripple_basics.h index ec947130cd..a24a883beb 100644 --- a/src/ripple_basics/ripple_basics.h +++ b/src/ripple_basics/ripple_basics.h @@ -80,12 +80,12 @@ using namespace beast; #include "utility/Time.h" #include "utility/UptimeTimer.h" -#include "containers/KeyCache.h" #include "containers/RangeSet.h" #include "containers/SyncUnorderedMap.h" } +#include "containers/KeyCache.h" #include "containers/TaggedCache.h" #endif