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