Concurrent SHAMap code

Use shared lock for SHAMap itself and concurrent map for nodes
This commit is contained in:
JoelKatz
2013-12-13 15:14:03 -08:00
committed by Vinnie Falco
parent cd8234acba
commit 045beb5f36
9 changed files with 294 additions and 110 deletions

View File

@@ -1046,7 +1046,6 @@ Json::Value Ledger::getJson (int options)
{ {
Json::Value txns (Json::arrayValue); Json::Value txns (Json::arrayValue);
SHAMapTreeNode::TNType type; SHAMapTreeNode::TNType type;
SHAMap::ScopedLockType l (mTransactionMap->peekMutex (), __FILE__, __LINE__);
for (SHAMapItem::pointer item = mTransactionMap->peekFirstItem (type); !!item; for (SHAMapItem::pointer item = mTransactionMap->peekFirstItem (type); !!item;
item = mTransactionMap->peekNextItem (item->getTag (), type)) item = mTransactionMap->peekNextItem (item->getTag (), type))
@@ -1131,10 +1130,8 @@ void Ledger::setCloseTime (boost::posix_time::ptime ptm)
mCloseTime = iToSeconds (ptm); mCloseTime = iToSeconds (ptm);
} }
// XXX Use shared locks where possible?
LedgerStateParms Ledger::writeBack (LedgerStateParms parms, SLE::ref entry) LedgerStateParms Ledger::writeBack (LedgerStateParms parms, SLE::ref entry)
{ {
SHAMap::ScopedLockType l (mAccountStateMap->peekMutex (), __FILE__, __LINE__);
bool create = false; bool create = false;
if (!mAccountStateMap->hasItem (entry->getIndex ())) if (!mAccountStateMap->hasItem (entry->getIndex ()))
@@ -1187,8 +1184,6 @@ SLE::pointer Ledger::getSLEi (uint256 const& uId)
{ {
uint256 hash; uint256 hash;
SHAMap::ScopedLockType sl (mAccountStateMap->peekMutex (), __FILE__, __LINE__);
SHAMapItem::pointer node = mAccountStateMap->peekItem (uId, hash); SHAMapItem::pointer node = mAccountStateMap->peekItem (uId, hash);
if (!node) if (!node)

View File

@@ -44,6 +44,7 @@
#include <boost/tuple/tuple_comparison.hpp> #include <boost/tuple/tuple_comparison.hpp>
#include <boost/unordered_set.hpp> #include <boost/unordered_set.hpp>
#include <boost/weak_ptr.hpp> #include <boost/weak_ptr.hpp>
#include <boost/thread/shared_mutex.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -28,8 +28,7 @@ void SHAMap::DefaultMissingNodeHandler::operator() (uint32 refNUm)
SHAMap::SHAMap (SHAMapType t, uint32 seq, SHAMap::SHAMap (SHAMapType t, uint32 seq,
MissingNodeHandler missing_node_handler) MissingNodeHandler missing_node_handler)
: mLock (this, "SHAMap", __FILE__, __LINE__) : mSeq (seq)
, mSeq (seq)
, mLedgerSeq (0) , mLedgerSeq (0)
, mState (smsModifying) , mState (smsModifying)
, mType (t) , mType (t)
@@ -41,13 +40,12 @@ SHAMap::SHAMap (SHAMapType t, uint32 seq,
root = boost::make_shared<SHAMapTreeNode> (mSeq, SHAMapNode (0, uint256 ())); root = boost::make_shared<SHAMapTreeNode> (mSeq, SHAMapNode (0, uint256 ()));
root->makeInner (); root->makeInner ();
mTNByID[*root] = root; mTNByID.replace(*root, root);
} }
SHAMap::SHAMap (SHAMapType t, uint256 const& hash, SHAMap::SHAMap (SHAMapType t, uint256 const& hash,
MissingNodeHandler missing_node_handler) MissingNodeHandler missing_node_handler)
: mLock (this, "SHAMap", __FILE__, __LINE__) : mSeq (1)
, mSeq (1)
, mLedgerSeq (0) , mLedgerSeq (0)
, mState (smsSynching) , mState (smsSynching)
, mType (t) , mType (t)
@@ -58,7 +56,7 @@ SHAMap::SHAMap (SHAMapType t, uint256 const& hash,
root = boost::make_shared<SHAMapTreeNode> (mSeq, SHAMapNode (0, uint256 ())); root = boost::make_shared<SHAMapTreeNode> (mSeq, SHAMapNode (0, uint256 ()));
root->makeInner (); root->makeInner ();
mTNByID[*root] = root; mTNByID.replace(*root, root);
} }
TaggedCacheType< SHAMap::TNIndex, SHAMapTreeNode, UptimeTimerAdapter> TaggedCacheType< SHAMap::TNIndex, SHAMapTreeNode, UptimeTimerAdapter>
@@ -114,7 +112,7 @@ SHAMap::pointer SHAMap::snapShot (bool isMutable)
// Return a new SHAMap that is a snapshot of this one // Return a new SHAMap that is a snapshot of this one
// Initially most nodes are shared and CoW is forced where needed // Initially most nodes are shared and CoW is forced where needed
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
newMap.mSeq = mSeq; newMap.mSeq = mSeq;
newMap.mTNByID = mTNByID; newMap.mTNByID = mTNByID;
newMap.root = root; newMap.root = root;
@@ -122,15 +120,15 @@ SHAMap::pointer SHAMap::snapShot (bool isMutable)
if (!isMutable) if (!isMutable)
newMap.mState = smsImmutable; newMap.mState = smsImmutable;
// If the existing map has any nodes it might modify, unshare them now // If the existing map has any nodes it might modify, unshare ours now
if (mState != smsImmutable) if (mState != smsImmutable)
{ {
BOOST_FOREACH(NodeMap::value_type& nodeIt, mTNByID) BOOST_FOREACH(NodeMap::value_type& nodeIt, mTNByID.peekMap())
{ {
if (nodeIt.second->getSeq() == mSeq) if (nodeIt.second->getSeq() == mSeq)
{ // We might modify this node, so duplicate it in the snapShot { // We might modify this node, so duplicate it in the snapShot
SHAMapTreeNode::pointer newNode = boost::make_shared<SHAMapTreeNode> (*nodeIt.second, mSeq); SHAMapTreeNode::pointer newNode = boost::make_shared<SHAMapTreeNode> (*nodeIt.second, mSeq);
newMap.mTNByID[*newNode] = newNode; newMap.mTNByID.replace (*newNode, newNode);
if (newNode->isRoot ()) if (newNode->isRoot ())
newMap.root = newNode; newMap.root = newNode;
} }
@@ -212,13 +210,10 @@ void SHAMap::dirtyUp (std::stack<SHAMapTreeNode::pointer>& stack, uint256 const&
SHAMapTreeNode::pointer SHAMap::checkCacheNode (const SHAMapNode& iNode) SHAMapTreeNode::pointer SHAMap::checkCacheNode (const SHAMapNode& iNode)
{ {
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.find (iNode); SHAMapTreeNode::pointer ret = mTNByID.retrieve(iNode);
if (ret && (ret->getSeq()!= 0))
if (it == mTNByID.end ()) ret->touch (mSeq);
return SHAMapTreeNode::pointer (); return ret;
it->second->touch (mSeq);
return it->second;
} }
SHAMapTreeNode::pointer SHAMap::walkTo (uint256 const& id, bool modify) SHAMapTreeNode::pointer SHAMap::walkTo (uint256 const& id, bool modify)
@@ -311,13 +306,10 @@ SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNode& id, uint256 const& has
SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNode& id, uint256 const& hash) SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNode& id, uint256 const& hash)
{ {
// fast, but you do not hold a reference SHAMapTreeNode::pointer ret = mTNByID.retrieve (id);
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.find (id); if (!ret)
ret = fetchNodeExternalNT (id, hash);
if (it != mTNByID.end ()) return ret ? ret.get() : nullptr;
return it->second.get ();
return fetchNodeExternalNT (id, hash).get ();
} }
SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNode& id, uint256 const& hash, SHAMapSyncFilter* filter) SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNode& id, uint256 const& hash, SHAMapSyncFilter* filter)
@@ -335,7 +327,7 @@ SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNode& id, uint256 const& h
SHAMapTreeNode* node = getNodePointerNT (id, hash); SHAMapTreeNode* node = getNodePointerNT (id, hash);
if (!node && filter) if (!node && filter)
{ { // Our regular node store didn't have the node. See if the filter does
Blob nodeData; Blob nodeData;
if (filter->haveNode (id, hash, nodeData)) if (filter->haveNode (id, hash, nodeData))
@@ -343,8 +335,12 @@ SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNode& id, uint256 const& h
SHAMapTreeNode::pointer node = boost::make_shared<SHAMapTreeNode> ( SHAMapTreeNode::pointer node = boost::make_shared<SHAMapTreeNode> (
boost::cref (id), boost::cref (nodeData), 0, snfPREFIX, boost::cref (hash), true); boost::cref (id), boost::cref (nodeData), 0, snfPREFIX, boost::cref (hash), true);
canonicalize (hash, node); canonicalize (hash, node);
mTNByID[id] = node;
// Canonicalize the node with mTNByID to make sure all threads gets the same node
// If the node is new, tell the filter
if (mTNByID.canonicalize (id, &node))
filter->gotNode (true, id, hash, nodeData, node->getType ()); filter->gotNode (true, id, hash, nodeData, node->getType ());
return node.get (); return node.get ();
} }
} }
@@ -363,11 +359,12 @@ void SHAMap::returnNode (SHAMapTreeNode::pointer& node, bool modify)
{ {
// have a CoW // have a CoW
assert (node->getSeq () < mSeq); assert (node->getSeq () < mSeq);
assert (mState != smsImmutable);
node = boost::make_shared<SHAMapTreeNode> (*node, mSeq); // here's to the new node, same as the old node node = boost::make_shared<SHAMapTreeNode> (*node, mSeq); // here's to the new node, same as the old node
assert (node->isValid ()); assert (node->isValid ());
mTNByID[*node] = node; mTNByID.replace (*node, node);
if (node->isRoot ()) if (node->isRoot ())
root = node; root = node;
@@ -501,7 +498,8 @@ static const SHAMapItem::pointer no_item;
SHAMapItem::pointer SHAMap::peekFirstItem () SHAMapItem::pointer SHAMap::peekFirstItem ()
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* node = firstBelow (root.get ()); SHAMapTreeNode* node = firstBelow (root.get ());
if (!node) if (!node)
@@ -512,7 +510,8 @@ SHAMapItem::pointer SHAMap::peekFirstItem ()
SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type) SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* node = firstBelow (root.get ()); SHAMapTreeNode* node = firstBelow (root.get ());
if (!node) if (!node)
@@ -524,7 +523,8 @@ SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type)
SHAMapItem::pointer SHAMap::peekLastItem () SHAMapItem::pointer SHAMap::peekLastItem ()
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* node = lastBelow (root.get ()); SHAMapTreeNode* node = lastBelow (root.get ());
if (!node) if (!node)
@@ -543,7 +543,7 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id)
SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNType& type) SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNType& type)
{ {
// Get a pointer to the next item in the tree after a given item - item need not be in tree // Get a pointer to the next item in the tree after a given item - item need not be in tree
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
std::stack<SHAMapTreeNode::pointer> stack = getStack (id, true); std::stack<SHAMapTreeNode::pointer> stack = getStack (id, true);
@@ -583,7 +583,7 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNT
// Get a pointer to the previous item in the tree after a given item - item need not be in tree // Get a pointer to the previous item in the tree after a given item - item need not be in tree
SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id) SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
std::stack<SHAMapTreeNode::pointer> stack = getStack (id, true); std::stack<SHAMapTreeNode::pointer> stack = getStack (id, true);
@@ -621,7 +621,8 @@ SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id)
SHAMapItem::pointer SHAMap::peekItem (uint256 const& id) SHAMapItem::pointer SHAMap::peekItem (uint256 const& id)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* leaf = walkToPointer (id); SHAMapTreeNode* leaf = walkToPointer (id);
if (!leaf) if (!leaf)
@@ -632,7 +633,8 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id)
SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType& type) SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType& type)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* leaf = walkToPointer (id); SHAMapTreeNode* leaf = walkToPointer (id);
if (!leaf) if (!leaf)
@@ -644,7 +646,8 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType&
SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash) SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* leaf = walkToPointer (id); SHAMapTreeNode* leaf = walkToPointer (id);
if (!leaf) if (!leaf)
@@ -658,7 +661,7 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash)
bool SHAMap::hasItem (uint256 const& id) bool SHAMap::hasItem (uint256 const& id)
{ {
// does the tree have an item with this ID // does the tree have an item with this ID
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* leaf = walkToPointer (id); SHAMapTreeNode* leaf = walkToPointer (id);
return (leaf != NULL); return (leaf != NULL);
@@ -667,7 +670,7 @@ bool SHAMap::hasItem (uint256 const& id)
bool SHAMap::delItem (uint256 const& id) bool SHAMap::delItem (uint256 const& id)
{ {
// delete the item with this ID // delete the item with this ID
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
assert (mState != smsImmutable); assert (mState != smsImmutable);
std::stack<SHAMapTreeNode::pointer> stack = getStack (id, true); std::stack<SHAMapTreeNode::pointer> stack = getStack (id, true);
@@ -748,7 +751,7 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta
SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE :
(hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM);
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
assert (mState != smsImmutable); assert (mState != smsImmutable);
std::stack<SHAMapTreeNode::pointer> stack = getStack (tag, true); std::stack<SHAMapTreeNode::pointer> stack = getStack (tag, true);
@@ -773,7 +776,7 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta
SHAMapTreeNode::pointer newNode = SHAMapTreeNode::pointer newNode =
boost::make_shared<SHAMapTreeNode> (node->getChildNodeID (branch), item, type, mSeq); boost::make_shared<SHAMapTreeNode> (node->getChildNodeID (branch), item, type, mSeq);
if (!mTNByID.emplace (SHAMapNode (*newNode), newNode).second) if (!mTNByID.peekMap().emplace (SHAMapNode (*newNode), newNode).second)
{ {
WriteLog (lsFATAL, SHAMap) << "Node: " << *node; WriteLog (lsFATAL, SHAMap) << "Node: " << *node;
WriteLog (lsFATAL, SHAMap) << "NewNode: " << *newNode; WriteLog (lsFATAL, SHAMap) << "NewNode: " << *newNode;
@@ -802,7 +805,7 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta
boost::make_shared<SHAMapTreeNode> (mSeq, node->getChildNodeID (b1)); boost::make_shared<SHAMapTreeNode> (mSeq, node->getChildNodeID (b1));
newNode->makeInner (); newNode->makeInner ();
if (!mTNByID.emplace (SHAMapNode (*newNode), newNode).second) if (!mTNByID.peekMap().emplace (SHAMapNode (*newNode), newNode).second)
assert (false); assert (false);
stack.push (node); stack.push (node);
@@ -816,7 +819,7 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta
boost::make_shared<SHAMapTreeNode> (node->getChildNodeID (b1), item, type, mSeq); boost::make_shared<SHAMapTreeNode> (node->getChildNodeID (b1), item, type, mSeq);
assert (newNode->isValid () && newNode->isLeaf ()); assert (newNode->isValid () && newNode->isLeaf ());
if (!mTNByID.emplace (SHAMapNode (*newNode), newNode).second) if (!mTNByID.peekMap().emplace (SHAMapNode (*newNode), newNode).second)
assert (false); assert (false);
node->setChildHash (b1, newNode->getNodeHash ()); // OPTIMIZEME hash op not needed node->setChildHash (b1, newNode->getNodeHash ()); // OPTIMIZEME hash op not needed
@@ -825,7 +828,7 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta
newNode = boost::make_shared<SHAMapTreeNode> (node->getChildNodeID (b2), otherItem, type, mSeq); newNode = boost::make_shared<SHAMapTreeNode> (node->getChildNodeID (b2), otherItem, type, mSeq);
assert (newNode->isValid () && newNode->isLeaf ()); assert (newNode->isValid () && newNode->isLeaf ());
if (!mTNByID.emplace (SHAMapNode (*newNode), newNode).second) if (!mTNByID.peekMap().emplace (SHAMapNode (*newNode), newNode).second)
assert (false); assert (false);
node->setChildHash (b2, newNode->getNodeHash ()); node->setChildHash (b2, newNode->getNodeHash ());
@@ -846,7 +849,7 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM
// can't change the tag but can change the hash // can't change the tag but can change the hash
uint256 tag = item->getTag (); uint256 tag = item->getTag ();
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
assert (mState != smsImmutable); assert (mState != smsImmutable);
std::stack<SHAMapTreeNode::pointer> stack = getStack (tag, true); std::stack<SHAMapTreeNode::pointer> stack = getStack (tag, true);
@@ -891,6 +894,12 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal (const SHAMapNode& id, uint256
return ret; return ret;
} }
/** Look at the cache and back end (things external to this SHAMap) to
find a tree node. Only a read lock is required because mTNByID has its
own, internal synchronization. Every thread calling this function must
get a shared pointer to the same underlying node.
This function does not throw.
*/
SHAMapTreeNode::pointer SHAMap::fetchNodeExternalNT (const SHAMapNode& id, uint256 const& hash) SHAMapTreeNode::pointer SHAMap::fetchNodeExternalNT (const SHAMapNode& id, uint256 const& hash)
{ {
SHAMapTreeNode::pointer ret; SHAMapTreeNode::pointer ret;
@@ -898,6 +907,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternalNT (const SHAMapNode& id, uint2
if (!getApp().running ()) if (!getApp().running ())
return ret; return ret;
// Check the cache of shared, immutable tree nodes
ret = getCache (hash, id); ret = getCache (hash, id);
if (ret) if (ret)
{ // The node was found in the TreeNodeCache { // The node was found in the TreeNodeCache
@@ -920,6 +930,8 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternalNT (const SHAMapNode& id, uint2
try try
{ {
// We make this node immutable (seq == 0) so that it can be shared
// CoW is needed if it is modified
ret = boost::make_shared<SHAMapTreeNode> (id, obj->getData (), 0, snfPREFIX, hash, true); ret = boost::make_shared<SHAMapTreeNode> (id, obj->getData (), 0, snfPREFIX, hash, true);
if (id != *ret) if (id != *ret)
@@ -936,6 +948,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternalNT (const SHAMapNode& id, uint2
return SHAMapTreeNode::pointer (); return SHAMapTreeNode::pointer ();
} }
// Share this immutable tree node in thre TreeNodeCache
canonicalize (hash, ret); canonicalize (hash, ret);
} }
catch (...) catch (...)
@@ -947,11 +960,11 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternalNT (const SHAMapNode& id, uint2
if (id.isRoot ()) // it is legal to replace an existing root if (id.isRoot ()) // it is legal to replace an existing root
{ {
mTNByID[id] = ret; mTNByID.replace(id, ret);
root = ret; root = ret;
} }
else if (!mTNByID.emplace (id, ret).second) else // Make sure other threads get pointers to the same underlying object
assert (false); mTNByID.canonicalize (id, &ret);
return ret; return ret;
} }
@@ -985,7 +998,7 @@ bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter)
root = boost::make_shared<SHAMapTreeNode> (SHAMapNode (), nodeData, root = boost::make_shared<SHAMapTreeNode> (SHAMapNode (), nodeData,
mSeq - 1, snfPREFIX, hash, true); mSeq - 1, snfPREFIX, hash, true);
mTNByID[*root] = root; mTNByID.replace(*root, root);
filter->gotNode (true, SHAMapNode (), hash, nodeData, root->getType ()); filter->gotNode (true, SHAMapNode (), hash, nodeData, root->getType ());
} }
@@ -1036,7 +1049,7 @@ int SHAMap::flushDirty (NodeMap& map, int maxNodes, NodeObjectType t, uint32 seq
boost::shared_ptr<SHAMap::NodeMap> SHAMap::disarmDirty () boost::shared_ptr<SHAMap::NodeMap> SHAMap::disarmDirty ()
{ {
// stop saving dirty nodes // stop saving dirty nodes
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
boost::shared_ptr<NodeMap> ret; boost::shared_ptr<NodeMap> ret;
ret.swap (mDirtyNodes); ret.swap (mDirtyNodes);
@@ -1072,17 +1085,21 @@ SHAMapTreeNode::pointer SHAMap::getNode (const SHAMapNode& nodeID)
// It throws if the map is incomplete // It throws if the map is incomplete
SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNode& nodeID) SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNode& nodeID)
{ {
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.find (nodeID); SHAMapTreeNode::pointer nodeptr = mTNByID.retrieve (nodeID);
if (it != mTNByID.end()) if (nodeptr)
{ {
it->second->touch(mSeq); SHAMapTreeNode* ret = nodeptr.get ();
return it->second.get(); ret->touch(mSeq);
return ret;
} }
SHAMapTreeNode* node = root.get(); SHAMapTreeNode* node = root.get();
while (nodeID != *node) while (nodeID != *node)
{ {
if (node->isLeaf ())
return NULL;
int branch = node->selectBranch (nodeID.getNodeID ()); int branch = node->selectBranch (nodeID.getNodeID ());
assert (branch >= 0); assert (branch >= 0);
@@ -1101,7 +1118,8 @@ bool SHAMap::getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeF
// Return the path of nodes to the specified index in the specified format // Return the path of nodes to the specified index in the specified format
// Return value: true = node present, false = node not present // Return value: true = node present, false = node not present
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* inNode = root.get (); SHAMapTreeNode* inNode = root.get ();
while (!inNode->isLeaf ()) while (!inNode->isLeaf ())
@@ -1131,13 +1149,13 @@ bool SHAMap::getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeF
void SHAMap::dropCache () void SHAMap::dropCache ()
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
assert (mState == smsImmutable); assert (mState == smsImmutable);
mTNByID.clear (); mTNByID.clear ();
if (root) if (root)
mTNByID[*root] = root; mTNByID.canonicalize(*root, &root);
} }
void SHAMap::dropBelow (SHAMapTreeNode* d) void SHAMap::dropBelow (SHAMapTreeNode* d)
@@ -1151,10 +1169,10 @@ void SHAMap::dropBelow (SHAMapTreeNode* d)
void SHAMap::dump (bool hash) void SHAMap::dump (bool hash)
{ {
WriteLog (lsINFO, SHAMap) << " MAP Contains"; WriteLog (lsINFO, SHAMap) << " MAP Contains";
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
for (boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.begin (); for (boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.peekMap().begin ();
it != mTNByID.end (); ++it) it != mTNByID.peekMap().end (); ++it)
{ {
WriteLog (lsINFO, SHAMap) << it->second->getString (); WriteLog (lsINFO, SHAMap) << it->second->getString ();
CondLog (hash, lsINFO, SHAMap) << it->second->getNodeHash (); CondLog (hash, lsINFO, SHAMap) << it->second->getNodeHash ();

View File

@@ -57,8 +57,9 @@ public:
typedef std::map<uint256, DeltaItem> Delta; typedef std::map<uint256, DeltaItem> Delta;
typedef boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> NodeMap; typedef boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> NodeMap;
typedef RippleRecursiveMutex LockType; typedef boost::shared_mutex LockType;
typedef LockType::ScopedLockType ScopedLockType; typedef boost::shared_lock<LockType> ScopedReadLockType;
typedef boost::unique_lock<LockType> ScopedWriteLockType;
public: public:
// build new map // build new map
@@ -86,12 +87,6 @@ public:
mLedgerSeq = lseq; mLedgerSeq = lseq;
} }
// hold the map stable across operations
LockType const& peekMutex () const
{
return mLock;
}
bool hasNode (const SHAMapNode & id); bool hasNode (const SHAMapNode & id);
bool fetchRoot (uint256 const & hash, SHAMapSyncFilter * filter); bool fetchRoot (uint256 const & hash, SHAMapSyncFilter * filter);
@@ -148,9 +143,9 @@ public:
assert (mState != smsInvalid); assert (mState != smsInvalid);
mState = smsImmutable; mState = smsImmutable;
} }
void clearImmutable () bool isImmutable ()
{ {
mState = smsModifying; return mState == smsImmutable;
} }
bool isSynching () const bool isSynching () const
{ {
@@ -192,8 +187,8 @@ public:
} }
// overloads for backed maps // overloads for backed maps
boost::shared_ptr<SHAMapTreeNode> fetchNodeExternal (const SHAMapNode & id, uint256 const & hash); // throws SHAMapTreeNode::pointer fetchNodeExternal (const SHAMapNode & id, uint256 const & hash); // throws
boost::shared_ptr<SHAMapTreeNode> fetchNodeExternalNT (const SHAMapNode & id, uint256 const & hash); // no throw SHAMapTreeNode::pointer fetchNodeExternalNT (const SHAMapNode & id, uint256 const & hash); // no throw
bool operator== (const SHAMap & s) bool operator== (const SHAMap & s)
{ {
@@ -277,15 +272,15 @@ private:
void visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function); void visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function);
private: private:
#if 1
LockType mLock; // This lock protects key SHAMap structures.
#else // One may change anything with a write lock.
// With a read lock, one may not invalidate pointers to existing members of mTNByID
mutable LockType mLock; mutable LockType mLock;
#endif
uint32 mSeq; uint32 mSeq;
uint32 mLedgerSeq; // sequence number of ledger this is part of uint32 mLedgerSeq; // sequence number of ledger this is part of
NodeMap mTNByID; SyncUnorderedMapType< SHAMapNode, SHAMapTreeNode::pointer > mTNByID;
boost::shared_ptr<NodeMap> mDirtyNodes; boost::shared_ptr<NodeMap> mDirtyNodes;
SHAMapTreeNode::pointer root; SHAMapTreeNode::pointer root;
SHAMapState mState; SHAMapState mState;

View File

@@ -138,7 +138,7 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
std::stack<SHAMapDeltaNode> nodeStack; // track nodes we've pushed std::stack<SHAMapDeltaNode> nodeStack; // track nodes we've pushed
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
if (getHash () == otherMap->getHash ()) if (getHash () == otherMap->getHash ())
return true; return true;
@@ -236,7 +236,7 @@ void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissi
{ {
std::stack<SHAMapTreeNode::pointer> nodeStack; std::stack<SHAMapTreeNode::pointer> nodeStack;
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
if (!root->isInner ()) // root is only node, and we have it if (!root->isInner ()) // root is only node, and we have it
return; return;

View File

@@ -25,12 +25,9 @@ KeyCache <uint256, UptimeTimerAdapter> SHAMap::fullBelowCache ("fullBelowCache",
void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> function) void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> function)
{ {
SHAMap::pointer snap; // Make a snapshot of this map so we don't need to hold
{ // a lock on the map we're visiting
ScopedLockType sl (mLock, __FILE__, __LINE__); snapShot (false)->visitLeavesInternal (function);
snap = snapShot (false);
}
snap->visitLeavesInternal(function);
} }
void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function) void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function)
@@ -95,10 +92,13 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
} }
} }
/** Get a list of node IDs and hashes for nodes that are part of this SHAMap but not available locally.
The filter can hold alternate sources of nodes that are not permanently stored locally
*/
void SHAMap::getMissingNodes (std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max, void SHAMap::getMissingNodes (std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
assert (root->isValid ()); assert (root->isValid ());
assert (root->getNodeHash().isNonZero ()); assert (root->getNodeHash().isNonZero ());
@@ -192,7 +192,7 @@ bool SHAMap::getNodeFat (const SHAMapNode& wanted, std::vector<SHAMapNode>& node
std::list<Blob >& rawNodes, bool fatRoot, bool fatLeaves) std::list<Blob >& rawNodes, bool fatRoot, bool fatLeaves)
{ {
// Gets a node and some of its children // Gets a node and some of its children
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
SHAMapTreeNode* node = getNodePointer(wanted); SHAMapTreeNode* node = getNodePointer(wanted);
@@ -247,7 +247,7 @@ bool SHAMap::getNodeFat (const SHAMapNode& wanted, std::vector<SHAMapNode>& node
bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format) bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
root->addRaw (s, format); root->addRaw (s, format);
return true; return true;
} }
@@ -255,7 +255,7 @@ bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
// we already have a root node // we already have a root node
if (root->getNodeHash ().isNonZero ()) if (root->getNodeHash ().isNonZero ())
@@ -276,7 +276,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
#endif #endif
root = node; root = node;
mTNByID[*root] = root; mTNByID.replace(*root, root);
if (root->isLeaf()) if (root->isLeaf())
clearSynching (); clearSynching ();
@@ -294,7 +294,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format, SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedWriteLockType sl (mLock);
// we already have a root node // we already have a root node
if (root->getNodeHash ().isNonZero ()) if (root->getNodeHash ().isNonZero ())
@@ -312,7 +312,7 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
return SHAMapAddNode::invalid (); return SHAMapAddNode::invalid ();
root = node; root = node;
mTNByID[*root] = root; mTNByID.replace(*root, root);
if (root->isLeaf()) if (root->isLeaf())
clearSynching (); clearSynching ();
@@ -329,6 +329,8 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
SHAMapAddNode SHAMap::addKnownNode (const SHAMapNode& node, Blob const& rawNode, SHAMapSyncFilter* filter) SHAMapAddNode SHAMap::addKnownNode (const SHAMapNode& node, Blob const& rawNode, SHAMapSyncFilter* filter)
{ {
ScopedWriteLockType sl (mLock);
// return value: true=okay, false=error // return value: true=okay, false=error
assert (!node.isRoot ()); assert (!node.isRoot ());
@@ -338,8 +340,6 @@ SHAMapAddNode SHAMap::addKnownNode (const SHAMapNode& node, Blob const& rawNode,
return SHAMapAddNode::duplicate (); return SHAMapAddNode::duplicate ();
} }
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (checkCacheNode (node)) // Do we already have this node? if (checkCacheNode (node)) // Do we already have this node?
return SHAMapAddNode::duplicate (); return SHAMapAddNode::duplicate ();
@@ -383,14 +383,13 @@ SHAMapAddNode SHAMap::addKnownNode (const SHAMapNode& node, Blob const& rawNode,
canonicalize (iNode->getChildHash (branch), newNode); canonicalize (iNode->getChildHash (branch), newNode);
if (filter) if (mTNByID.canonicalize(node, &newNode) && filter)
{ {
Serializer s; Serializer s;
newNode->addRaw (s, snfPREFIX); newNode->addRaw (s, snfPREFIX);
filter->gotNode (false, node, iNode->getChildHash (branch), s.modData (), newNode->getType ()); filter->gotNode (false, node, iNode->getChildHash (branch), s.modData (), newNode->getType ());
} }
mTNByID[node] = newNode;
return SHAMapAddNode::useful (); return SHAMapAddNode::useful ();
} }
iNode = nextNode; iNode = nextNode;
@@ -404,7 +403,7 @@ bool SHAMap::deepCompare (SHAMap& other)
{ {
// Intended for debug/test only // Intended for debug/test only
std::stack<SHAMapTreeNode::pointer> stack; std::stack<SHAMapTreeNode::pointer> stack;
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
stack.push (root); stack.push (root);
@@ -472,11 +471,14 @@ bool SHAMap::deepCompare (SHAMap& other)
return true; return true;
} }
/** Does this map have this inner node?
You must hold a read lock to call this function
*/
bool SHAMap::hasInnerNode (const SHAMapNode& nodeID, uint256 const& nodeHash) bool SHAMap::hasInnerNode (const SHAMapNode& nodeID, uint256 const& nodeHash)
{ {
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.find (nodeID); SHAMapTreeNode::pointer ptr = mTNByID.retrieve (nodeID);
if (it != mTNByID.end()) if (ptr)
return it->second->getNodeHash() == nodeHash; return ptr->getNodeHash() == nodeHash;
SHAMapTreeNode* node = root.get (); SHAMapTreeNode* node = root.get ();
@@ -493,6 +495,9 @@ bool SHAMap::hasInnerNode (const SHAMapNode& nodeID, uint256 const& nodeHash)
return node->getNodeHash () == nodeHash; return node->getNodeHash () == nodeHash;
} }
/** Does this map have this leaf node?
You must hold a read lock to call this function
*/
bool SHAMap::hasLeafNode (uint256 const& tag, uint256 const& nodeHash) bool SHAMap::hasLeafNode (uint256 const& tag, uint256 const& nodeHash)
{ {
SHAMapTreeNode* node = root.get (); SHAMapTreeNode* node = root.get ();
@@ -534,14 +539,14 @@ std::list<SHAMap::fetchPackEntry_t> SHAMap::getFetchPack (SHAMap* have, bool inc
void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max, void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
std::function<void (const uint256&, const Blob&)> func) std::function<void (const uint256&, const Blob&)> func)
{ {
ScopedLockType ul1 (mLock, __FILE__, __LINE__); ScopedReadLockType ul1 (mLock);
std::unique_ptr <LockType::ScopedTryLockType> ul2; std::unique_ptr <ScopedReadLockType> ul2;
if (have) if (have)
{ {
// VFALCO NOTE This looks like a mess. A dynamically allocated scoped lock? // VFALCO NOTE This looks like a mess. A dynamically allocated scoped lock?
ul2.reset (new LockType::ScopedTryLockType (have->mLock, __FILE__, __LINE__)); ul2.reset (new ScopedReadLockType (have->mLock, boost::try_to_lock));
if (! ul2->owns_lock ()) if (! ul2->owns_lock ())
{ {
@@ -565,6 +570,7 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
Serializer s; Serializer s;
root->addRaw (s, snfPREFIX); root->addRaw (s, snfPREFIX);
func (boost::cref(root->getNodeHash ()), boost::cref(s.peekData ())); func (boost::cref(root->getNodeHash ()), boost::cref(s.peekData ()));
--max;
} }
return; return;
@@ -613,7 +619,8 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
std::list<Blob > SHAMap::getTrustedPath (uint256 const& index) std::list<Blob > SHAMap::getTrustedPath (uint256 const& index)
{ {
ScopedLockType sl (mLock, __FILE__, __LINE__); ScopedReadLockType sl (mLock);
std::stack<SHAMapTreeNode::pointer> stack = SHAMap::getStack (index, false); std::stack<SHAMapTreeNode::pointer> stack = SHAMap::getStack (index, false);
if (stack.empty () || !stack.top ()->isLeaf ()) if (stack.empty () || !stack.top ()->isLeaf ())

View File

@@ -74,6 +74,7 @@ public:
} }
void touch (uint32 s) void touch (uint32 s)
{ {
if (mSeq != 0)
mAccessSeq = s; mAccessSeq = s;
} }
uint256 const& getNodeHash () const uint256 const& getNodeHash () const

View File

@@ -0,0 +1,166 @@
//------------------------------------------------------------------------------
/*
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_SYNC_UNORDERED_MAP_H
#define RIPPLE_SYNC_UNORDERED_MAP_H
// Common base
class SyncUnorderedMap
{
public:
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
};
/** This is a synchronized unordered map.
It is useful for cases where an unordered map contains all
or a subset of an unchanging data set.
*/
template <typename c_Key, typename c_Data>
class SyncUnorderedMapType : public SyncUnorderedMap
{
public:
typedef c_Key key_type;
typedef c_Data data_type;
typedef boost::unordered_map<c_Key, c_Data> map_type;
class iterator
{
public:
bool operator== (const iterator& i) { return it == i.it; }
bool operator!= (const iterator& i) { return it != i.it; }
key_type const& key () { return it.first; }
data_type& data () { return it.second; }
protected:
typename map_type::iterator it;
};
public:
typedef SyncUnorderedMap::LockType LockType;
typedef SyncUnorderedMap::ScopedLockType ScopedLockType;
SyncUnorderedMapType (const SyncUnorderedMapType& m)
{
ScopedLockType sl (m.mLock, __FILE__, __LINE__);
mMap = m.mMap;
}
SyncUnorderedMapType ()
{ ; }
// Operations that are not inherently synchronous safe
// (Usually because they can change the contents of the map or
// invalidated its members.)
void operator= (const SyncUnorderedMapType& m)
{
ScopedLockType sl (m.mLock, __FILE__, __LINE__);
mMap = m.mMap;
}
void clear ()
{
mMap.clear();
}
int erase (key_type const& key)
{
return mMap.erase (key);
}
void erase (iterator& iterator)
{
mMap.erase (iterator.it);
}
void replace (key_type const& key, data_type const& data)
{
mMap[key] = data;
}
void rehash (int s)
{
mMap.rehash (s);
}
map_type& peekMap ()
{
return mMap;
}
// Operations that are inherently synchronous safe
std::size_t size () const
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mMap.size ();
}
// If the value is already in the map, replace it with the old value
// Otherwise, store the value passed.
// Returns 'true' if the value was added to the map
bool canonicalize (key_type const& key, data_type* value)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
typename std::pair < typename map_type::iterator, bool > it =
mMap.insert (typename map_type::value_type (key, *value));
if (!it.second) // Value was not added, take existing value
*value = it.first->second;
return it.second;
}
// Retrieve the existing value from the map.
// If none, return an 'empty' value
data_type retrieve (key_type const& key)
{
data_type ret;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
typename map_type::iterator it = mMap.find (key);
if (it != mMap.end ())
ret = it->second;
}
return ret;
}
private:
map_type mMap;
mutable LockType mLock;
};
namespace detail
{
template <typename Key, typename Value>
struct Destroyer <SyncUnorderedMapType <Key, Value> >
{
static void destroy (SyncUnorderedMapType <Key, Value>& v)
{
v.clear ();
}
};
}
#endif

View File

@@ -85,6 +85,7 @@ using namespace beast;
#include "containers/RangeSet.h" #include "containers/RangeSet.h"
#include "containers/BlackList.h" #include "containers/BlackList.h"
#include "containers/TaggedCache.h" #include "containers/TaggedCache.h"
#include "containers/SyncUnorderedMap.h"
} }