SHAMap performance improvements (RIPD-434)

This reworks the way SHAMaps are stored, flushed, backed, and
traversed. Rather than storing the linkages in the SHAMap itself,
that information is now stored in the nodes. This makes
snapshotting much cheaper and also allows traverse work done on
behalf of one SHAMap to be used by other SHAMaps that share inner
nodes with that SHAMap.

When a SHAMap is modified, nodes are modified all the way up to the
root. This means that the modified nodes in a SHAMap can easily be
traversed for flushing. So they don't need to be separately tracked.

Summary
* Remove mTNByID
* Remove mDirtyNodes
* Much faster traverses
* Much Faster snapshots
* New algorithm for flushing
* New vistNodes/visitLeaves
* Avoid I/O if a map is unbacked
This commit is contained in:
David Schwartz
2014-07-31 16:38:58 -07:00
committed by Tom Ritchford
parent d26241de0e
commit fc560179e0
15 changed files with 889 additions and 1064 deletions

View File

@@ -1027,35 +1027,19 @@ private:
// Set up to write SHAMap changes to our database,
// perform updates, extract changes
newLCL->peekTransactionMap ()->armDirty (); // start tracking changes
newLCL->peekAccountStateMap ()->armDirty ();
WriteLog (lsDEBUG, LedgerConsensus)
<< "Applying consensus set transactions to the"
<< " last closed ledger";
applyTransactions (set, newLCL, newLCL, retriableTransactions, false);
newLCL->updateSkipList ();
newLCL->setClosed ();
std::shared_ptr<SHAMap::DirtySet> acctNodes
= newLCL->peekAccountStateMap ()->disarmDirty (); // stop tracking changes
std::shared_ptr<SHAMap::DirtySet> txnNodes
= newLCL->peekTransactionMap ()->disarmDirty ();
// write out dirty nodes (temporarily done here)
int fc;
while ((fc = newLCL->peekAccountStateMap()->flushDirty (
*acctNodes, 256, hotACCOUNT_NODE, newLCL->getLedgerSeq ())) > 0)
{
WriteLog (lsTRACE, LedgerConsensus)
<< "Flushed " << fc << " dirty state nodes";
}
while ((fc = newLCL->peekTransactionMap()->flushDirty (
*txnNodes, 256, hotTRANSACTION_NODE, newLCL->getLedgerSeq ())) > 0)
{
WriteLog (lsTRACE, LedgerConsensus)
<< "Flushed " << fc << " dirty transaction nodes";
}
int asf = newLCL->peekAccountStateMap ()->flushDirty (
hotACCOUNT_NODE, newLCL->getLedgerSeq());
int tmf = newLCL->peekTransactionMap ()->flushDirty (
hotTRANSACTION_NODE, newLCL->getLedgerSeq());
WriteLog (lsDEBUG, LedgerConsensus) << "Flushed " << asf << " account and " <<
tmf << "transaction nodes";
// Accept ledger
newLCL->setAccepted (closeTime, mCloseResolution, closeTimeCorrect);

View File

@@ -1232,9 +1232,6 @@ Json::Value InboundLedger::getJson (int)
{
ret["have_state"] = mHaveState;
ret["have_transactions"] = mHaveTransactions;
if (!mHaveState)
ret["state_nodes"] = static_cast<Json::Value::UInt>
(mLedger->peekAccountStateMap()->size());
}
if (mAborted)

View File

@@ -57,14 +57,9 @@ Ledger::Ledger (RippleAddress const& masterID, std::uint64_t startAmount)
WriteLog (lsTRACE, Ledger)
<< "root account: " << startAccount->peekSLE ().getJson (0);
mAccountStateMap->armDirty ();
writeBack (lepCREATE, startAccount->getSLE ());
auto dirtyNodes = mAccountStateMap->disarmDirty();
mAccountStateMap->flushDirty (
*dirtyNodes, 256, hotACCOUNT_NODE, mLedgerSeq);
// TODO(tom): why 256?
mAccountStateMap->flushDirty (hotACCOUNT_NODE, mLedgerSeq);
initializeFees ();
}
@@ -243,16 +238,12 @@ Ledger::~Ledger ()
{
if (mTransactionMap)
{
logTimedDestroy <Ledger> (mTransactionMap,
"mTransactionMap with " +
std::to_string(mTransactionMap->size ()) + " items");
logTimedDestroy <Ledger> (mTransactionMap, "mTransactionMap");
}
if (mAccountStateMap)
{
logTimedDestroy <Ledger> (mAccountStateMap,
"mAccountStateMap with " +
std::to_string (mAccountStateMap->size ()) + " items");
logTimedDestroy <Ledger> (mAccountStateMap, "mAccountStateMap");
}
}

View File

@@ -239,16 +239,6 @@ public:
{
return mAccountStateMap;
}
void dropCache ()
{
assert (isImmutable ());
if (mTransactionMap)
mTransactionMap->dropCache ();
if (mAccountStateMap)
mAccountStateMap->dropCache ();
}
// returns false on error
bool addSLE (SLE const& sle);

View File

@@ -305,8 +305,6 @@ public:
return false;
}
nodeLedger->dropCache();
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -114,11 +114,8 @@ public:
typedef std::pair<SHAMapItem::ref, SHAMapItem::ref> DeltaRef;
typedef std::map<uint256, DeltaItem> Delta;
typedef hash_map<SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash> NodeMap;
typedef hash_set<SHAMapNodeID, SHAMapNode_hash> DirtySet;
typedef boost::shared_mutex LockType;
typedef boost::shared_lock<LockType> ScopedReadLockType;
typedef boost::unique_lock<LockType> ScopedWriteLockType;
typedef std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> SharedPtrNodeStack;
public:
// build new map
@@ -138,17 +135,10 @@ public:
~SHAMap ();
std::size_t size () const noexcept
{
return mTNByID.size ();
}
// Returns a new map that's a snapshot of this one. Force CoW
// Returns a new map that's a snapshot of this one.
// Handles copy on write for mutable snapshots.
SHAMap::pointer snapShot (bool isMutable);
// Remove nodes from memory
void dropCache ();
void setLedgerSeq (std::uint32_t lseq)
{
mLedgerSeq = lseq;
@@ -159,7 +149,7 @@ public:
// normal hash access functions
bool hasItem (uint256 const& id);
bool delItem (uint256 const& id);
bool addItem (const SHAMapItem & i, bool isTransaction, bool hasMeta);
bool addItem (SHAMapItem const& i, bool isTransaction, bool hasMeta);
uint256 getHash () const
{
@@ -182,7 +172,9 @@ public:
SHAMapItem::pointer peekNextItem (uint256 const& );
SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type);
SHAMapItem::pointer peekPrevItem (uint256 const& );
void visitLeaves(std::function<void (SHAMapItem::ref)>);
void visitNodes (std::function<void (SHAMapTreeNode&)> const&);
void visitLeaves(std::function<void (SHAMapItem::ref)> const&);
// comparison/sync functions
void getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
@@ -191,11 +183,11 @@ public:
std::list<Blob >& rawNode, bool fatRoot, bool fatLeaves);
bool getRootNode (Serializer & s, SHANodeFormat format);
std::vector<uint256> getNeededHashes (int max, SHAMapSyncFilter * filter);
SHAMapAddNode addRootNode (uint256 const& hash, Blob const & rootNode, SHANodeFormat format,
SHAMapAddNode addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter * filter);
SHAMapAddNode addRootNode (Blob const & rootNode, SHANodeFormat format,
SHAMapAddNode addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter * filter);
SHAMapAddNode addKnownNode (const SHAMapNodeID & nodeID, Blob const & rawNode,
SHAMapAddNode addKnownNode (SHAMapNodeID const& nodeID, Blob const& rawNode,
SHAMapSyncFilter * filter);
// status functions
@@ -225,10 +217,8 @@ public:
// return value: true=successfully completed, false=too different
bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount);
int armDirty ();
int flushDirty (DirtySet & dirtySet, int maxNodes, NodeObjectType t,
std::uint32_t seq);
std::shared_ptr<DirtySet> disarmDirty ();
int flushDirty (NodeObjectType t, std::uint32_t seq);
int unshare ();
void walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
@@ -238,81 +228,105 @@ public:
void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function<void (uint256 const&, const Blob&)>);
void setTXMap ()
void setUnbacked ()
{
mTXMap = true;
mBacked = false;
}
void dump (bool withHashes = false);
private:
// trusted path operations - prove a particular node is in a particular ledger
std::list<Blob > getTrustedPath (uint256 const& index);
SHAMapTreeNode::pointer fetchNodeExternal (const SHAMapNodeID & id,
uint256 const& hash); // throws
SHAMapTreeNode::pointer fetchNodeExternalNT (const SHAMapNodeID & id,
uint256 const& hash); // no throw
bool getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format);
void dump (bool withHashes = false);
// tree node cache operations
SHAMapTreeNode::pointer getCache (uint256 const& hash);
void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&);
void dirtyUp (std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>& stack,
uint256 const& target, uint256 prevHash);
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>
// database operations
SHAMapTreeNode::pointer fetchNodeFromDB (uint256 const& hash);
SHAMapTreeNode::pointer fetchNodeNT (uint256 const& hash);
SHAMapTreeNode::pointer fetchNodeNT (
SHAMapNodeID const& id,
uint256 const& hash,
SHAMapSyncFilter *filter);
SHAMapTreeNode::pointer fetchNode (uint256 const& hash);
SHAMapTreeNode::pointer checkFilter (uint256 const& hash, SHAMapNodeID const& id,
SHAMapSyncFilter* filter);
/** Update hashes up to the root */
void dirtyUp (SharedPtrNodeStack& stack,
uint256 const& target, SHAMapTreeNode::pointer terminal);
/** Get the path from the root to the specified node */
SharedPtrNodeStack
getStack (uint256 const& id, bool include_nonmatching_leaf);
SHAMapTreeNode::pointer walkTo (uint256 const& id, bool modify);
/** Walk to the specified index, returning the node */
SHAMapTreeNode* walkToPointer (uint256 const& id);
SHAMapTreeNode::pointer checkCacheNode (const SHAMapNodeID&);
void returnNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID,
bool modify);
void trackNewNode (SHAMapTreeNode::pointer&, SHAMapNodeID const&);
SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id);
SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id, uint256 const& hash, bool modify);
SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id);
SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id, uint256 const& hash);
SHAMapTreeNode* getNodePointerNT (const SHAMapNodeID & id, uint256 const& hash);
SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter);
SHAMapTreeNode* getNodePointerNT (const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter);
SHAMapTreeNode* firstBelow (SHAMapTreeNode*, SHAMapNodeID);
SHAMapTreeNode* lastBelow (SHAMapTreeNode*, SHAMapNodeID);
/** Unshare the node, allowing it to be modified */
void unshareNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID);
// Non-blocking version of getNodePointerNT
SHAMapTreeNode* getNodeAsync (
const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter, bool& pending);
/** prepare a node to be modified before flushing */
void preFlushNode (SHAMapTreeNode::pointer& node);
SHAMapItem::pointer onlyBelow (SHAMapTreeNode*, SHAMapNodeID);
void eraseChildren (SHAMapTreeNode::pointer, SHAMapNodeID);
void dropBelow (SHAMapTreeNode*, SHAMapNodeID);
bool hasInnerNode (const SHAMapNodeID & nodeID, uint256 const& hash);
/** write and canonicalize modified node */
void writeNode (NodeObjectType t, std::uint32_t seq,
SHAMapTreeNode::pointer& node);
SHAMapTreeNode* firstBelow (SHAMapTreeNode*);
SHAMapTreeNode* lastBelow (SHAMapTreeNode*);
// Simple descent
// Get a child of the specified node
SHAMapTreeNode* descend (SHAMapTreeNode*, int branch);
SHAMapTreeNode* descendThrow (SHAMapTreeNode*, int branch);
SHAMapTreeNode::pointer descend (SHAMapTreeNode::ref, int branch);
SHAMapTreeNode::pointer descendThrow (SHAMapTreeNode::ref, int branch);
// Descend with filter
SHAMapTreeNode* descendAsync (SHAMapTreeNode* parent, int branch,
SHAMapNodeID const& childID, SHAMapSyncFilter* filter, bool& pending);
std::pair <SHAMapTreeNode*, SHAMapNodeID>
descend (SHAMapTreeNode* parent, SHAMapNodeID const& parentID,
int branch, SHAMapSyncFilter* filter);
// Non-storing
// Does not hook the returned node to its parent
SHAMapTreeNode::pointer descendNoStore (SHAMapTreeNode::ref, int branch);
/** If there is only one leaf below this node, get its contents */
SHAMapItem::pointer onlyBelow (SHAMapTreeNode*);
bool hasInnerNode (SHAMapNodeID const& nodeID, uint256 const& hash);
bool hasLeafNode (uint256 const& tag, uint256 const& hash);
bool walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID,
bool walkBranch (SHAMapTreeNode* node,
SHAMapItem::ref otherMapItem, bool isFirstMap,
Delta & differences, int & maxCount);
void visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function);
private:
int walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq);
// This lock protects key SHAMap structures.
// 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;
private:
FullBelowCache& m_fullBelowCache;
std::uint32_t mSeq;
std::uint32_t mLedgerSeq; // sequence number of ledger this is part of
SyncUnorderedMapType< SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash > mTNByID;
std::shared_ptr<DirtySet> mDirtyNodes;
TreeNodeCache& mTreeNodeCache;
SHAMapTreeNode::pointer root;
SHAMapState mState;
SHAMapType mType;
bool mTXMap; // Map of transactions without metadata
bool mBacked; // Map is backed by the database
MissingNodeHandler m_missing_node_handler;
};

View File

@@ -25,47 +25,29 @@ namespace ripple {
// branches with the same branch hash. A limit can be passed so
// that we will abort early if a node sends a map to us that
// makes no sense at all. (And our sync algorithm will avoid
// synchronizing matching brances too.)
// synchronizing matching branches too.)
class SHAMapDeltaNode
{
public:
SHAMapNodeID mNodeID;
uint256 mOurHash, mOtherHash;
SHAMapDeltaNode (const SHAMapNodeID& id, uint256 const& ourHash, uint256 const& otherHash) :
mNodeID (id), mOurHash (ourHash), mOtherHash (otherHash)
{
;
}
};
bool SHAMap::walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID,
bool SHAMap::walkBranch (SHAMapTreeNode* node,
SHAMapItem::ref otherMapItem, bool isFirstMap,
Delta& differences, int& maxCount)
{
// Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
std::stack<std::pair<SHAMapTreeNode*, SHAMapNodeID>> nodeStack;
nodeStack.push ({node, nodeID});
std::stack <SHAMapTreeNode*, std::vector<SHAMapTreeNode*>> nodeStack;
nodeStack.push ({node});
bool emptyBranch = !otherMapItem;
while (!nodeStack.empty ())
{
std::tie(node, nodeID) = nodeStack.top ();
node = nodeStack.top ();
nodeStack.pop ();
if (node->isInner ())
{
// This is an inner node, add all non-empty branches
uint256 childNodeHash;
for (int i = 0; i < 16; ++i)
{
SHAMapNodeID childNodeID = nodeID;
if (node->descend (i, childNodeID, childNodeHash))
nodeStack.push ({getNodePointer (childNodeID, childNodeHash),
childNodeID});
}
if (!node->isEmptyBranch (i))
nodeStack.push ({descendThrow (node, i)});
}
else
{
@@ -136,27 +118,23 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
assert (isValid () && otherMap && otherMap->isValid ());
std::stack<SHAMapDeltaNode> nodeStack; // track nodes we've pushed
ScopedReadLockType sl (mLock);
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapTreeNode*>;
std::stack <StackEntry, std::vector<StackEntry>> nodeStack; // track nodes we've pushed
if (getHash () == otherMap->getHash ())
return true;
nodeStack.push (SHAMapDeltaNode (SHAMapNodeID (), getHash (),
otherMap->getHash ()));
nodeStack.push ({root.get(), otherMap->root.get()});
while (!nodeStack.empty ())
{
SHAMapDeltaNode dNode (nodeStack.top ());
SHAMapTreeNode* ourNode = nodeStack.top().first;
SHAMapTreeNode* otherNode = nodeStack.top().second;
nodeStack.pop ();
SHAMapTreeNode* ourNode = getNodePointer (dNode.mNodeID, dNode.mOurHash);
SHAMapTreeNode* otherNode = otherMap->getNodePointer (dNode.mNodeID,
dNode.mOtherHash);
if (!ourNode || !otherNode)
{
assert (false);
throw SHAMapMissingNode (mType, dNode.mNodeID, uint256 ());
throw SHAMapMissingNode (mType, uint256 ());
}
if (ourNode->isLeaf () && otherNode->isLeaf ())
@@ -190,14 +168,14 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
}
else if (ourNode->isInner () && otherNode->isLeaf ())
{
if (!walkBranch (ourNode, dNode.mNodeID, otherNode->peekItem (),
true, differences, maxCount))
if (!walkBranch (ourNode, otherNode->peekItem (),
true, differences, maxCount))
return false;
}
else if (ourNode->isLeaf () && otherNode->isInner ())
{
if (!otherMap->walkBranch (otherNode, dNode.mNodeID,
ourNode->peekItem (), false, differences, maxCount))
if (!otherMap->walkBranch (otherNode, ourNode->peekItem (),
false, differences, maxCount))
return false;
}
else if (ourNode->isInner () && otherNode->isInner ())
@@ -208,10 +186,8 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
if (otherNode->isEmptyBranch (i))
{
// We have a branch, the other tree does not
SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i);
SHAMapTreeNode* iNode = getNodePointer (childNodeID,
ourNode->getChildHash (i));
if (!walkBranch (iNode, childNodeID,
SHAMapTreeNode* iNode = descendThrow (ourNode, i);
if (!walkBranch (iNode,
SHAMapItem::pointer (), true,
differences, maxCount))
return false;
@@ -219,20 +195,16 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
else if (ourNode->isEmptyBranch (i))
{
// The other tree has a branch, we do not
SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i);
SHAMapTreeNode* iNode =
otherMap->getNodePointer(childNodeID,
otherNode->getChildHash (i));
if (!otherMap->walkBranch (iNode, childNodeID,
otherMap->descendThrow(otherNode, i);
if (!otherMap->walkBranch (iNode,
SHAMapItem::pointer(),
false, differences, maxCount))
return false;
}
else // The two trees have different non-empty branches
nodeStack.push (SHAMapDeltaNode (
dNode.mNodeID.getChildNodeID (i),
ourNode->getChildHash (i),
otherNode->getChildHash (i)));
nodeStack.push ({descendThrow (ourNode, i),
otherMap->descendThrow (otherNode, i)});
}
}
else
@@ -244,42 +216,37 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
{
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> nodeStack;
ScopedReadLockType sl (mLock);
std::stack <SHAMapTreeNode::pointer,
std::vector <SHAMapTreeNode::pointer>> nodeStack;
if (!root->isInner ()) // root is only node, and we have it
return;
nodeStack.push ({root, SHAMapNodeID{}});
nodeStack.push (root);
while (!nodeStack.empty ())
{
SHAMapTreeNode::pointer node;
SHAMapNodeID nodeID;
std::tie(node, nodeID) = nodeStack.top ();
SHAMapTreeNode::pointer node = std::move (nodeStack.top());
nodeStack.pop ();
uint256 childNodeHash;
for (int i = 0; i < 16; ++i)
{
SHAMapNodeID childNodeID = nodeID;
if (node->descend (i, childNodeID, childNodeHash))
if (!node->isEmptyBranch (i))
{
try
{
SHAMapTreeNode::pointer d = getNode(childNodeID,
childNodeHash, false);
if (d->isInner ())
nodeStack.push ({d, childNodeID});
}
catch (SHAMapMissingNode& n)
{
missingNodes.push_back (n);
SHAMapTreeNode::pointer nextNode = descendNoStore (node, i);
if (--maxMissing <= 0)
return;
}
}
if (nextNode)
{
if (nextNode->isInner ())
nodeStack.push (std::move (nextNode));
}
else
{
missingNodes.emplace_back (mType, node->getChildHash (i));
if (--maxMissing <= 0)
return;
}
}
}
}
}

View File

@@ -24,16 +24,16 @@ std::ostream& operator<< (std::ostream& out, const SHAMapMissingNode& mn)
switch (mn.getMapType ())
{
case smtTRANSACTION:
out << "Missing/TXN(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")";
out << "Missing/TXN(" << mn.getNodeHash () << ")";
break;
case smtSTATE:
out << "Missing/STA(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")";
out << "Missing/STA(" << mn.getNodeHash () << ")";
break;
case smtFREE:
default:
out << "Missing/" << mn.getNodeID ();
out << "Missing/" << mn.getNodeHash ();
break;
};

View File

@@ -33,11 +33,9 @@ class SHAMapMissingNode : public std::runtime_error
{
public:
SHAMapMissingNode (SHAMapType t,
SHAMapNodeID const& nodeID,
uint256 const& nodeHash)
: std::runtime_error ("SHAMapMissingNode")
, mType (t)
, mNodeID (nodeID)
, mNodeHash (nodeHash)
{
}
@@ -51,11 +49,6 @@ public:
return mType;
}
SHAMapNodeID const& getNodeID () const
{
return mNodeID;
}
uint256 const& getNodeHash () const
{
return mNodeHash;
@@ -63,7 +56,6 @@ public:
private:
SHAMapType mType;
SHAMapNodeID mNodeID;
uint256 mNodeHash;
};

View File

@@ -134,6 +134,7 @@ std::string SHAMapNodeID::getRawString () const
SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const
{
assert ((m >= 0) && (m < 16));
assert (mDepth <= 64);
uint256 child (mNodeID);
child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4);

View File

@@ -26,46 +26,52 @@ namespace ripple {
static const uint256 uZero;
void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> function)
static void visitLeavesHelper (
std::function <void (SHAMapItem::ref)> const& function,
SHAMapTreeNode& node)
{
// Make a snapshot of this map so we don't need to hold
// a lock on the map we're visiting
snapShot (false)->visitLeavesInternal (function);
// Adapt visitNodes to visitLeaves
if (!node.isInner ())
function (node.peekItem ());
}
void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function)
void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> const& leafFunction)
{
visitNodes (std::bind (visitLeavesHelper,
std::cref (leafFunction), std::placeholders::_1));
}
void SHAMap::visitNodes(std::function<void (SHAMapTreeNode&)> const& function)
{
// Visit every node in a SHAMap
assert (root->isValid ());
if (!root || root->isEmpty ())
return;
if (!root->isInner ())
{
function (root->peekItem ());
return;
}
function (*root);
std::stack<std::tuple<int, SHAMapTreeNode*, SHAMapNodeID>> stack;
SHAMapTreeNode* node = root.get ();
SHAMapNodeID nodeID;
if (!root->isInner ())
return;
using StackEntry = std::pair <int, SHAMapTreeNode::pointer>;
std::stack <StackEntry, std::vector <StackEntry>> stack;
SHAMapTreeNode::pointer node = root;
int pos = 0;
while (1)
{
while (pos < 16)
{
SHAMapNodeID childID = nodeID;
uint256 childHash;
if (node->descend (pos, childID, childHash))
if (!node->isEmptyBranch (pos))
{
SHAMapTreeNode* child = getNodePointer (childID, childHash);
SHAMapTreeNode::pointer child = descendNoStore (node, pos);
function (*child);
if (child->isLeaf ())
{
function (child->peekItem ());
mTNByID.erase (childID); // don't need this leaf anymore
++pos;
}
else
{
// If there are no more children, don't push this node
@@ -75,14 +81,11 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
if (pos != 15)
{
// save next position to resume at
stack.push (std::make_tuple(pos + 1, node, nodeID));
stack.push (std::make_pair(pos + 1, std::move (node)));
}
else
mTNByID.erase (nodeID); // don't need this inner node anymore
// descend to the child's first position
node = child;
nodeID = childID;
pos = 0;
}
}
@@ -92,13 +95,10 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
}
}
// We are done with this inner node
mTNByID.erase (nodeID);
if (stack.empty ())
break;
std::tie(pos, node, nodeID) = stack.top ();
std::tie(pos, node) = stack.top ();
stack.pop ();
}
}
@@ -110,12 +110,9 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
SHAMapSyncFilter* filter)
{
ScopedReadLockType sl (mLock);
assert (root->isValid ());
assert (root->getNodeHash().isNonZero ());
if (root->isFullBelow ())
{
clearSynching ();
@@ -136,11 +133,12 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
while (1)
{
std::vector <std::pair <SHAMapNodeID, uint256>> deferredReads;
std::vector <std::tuple <SHAMapTreeNode*, int, SHAMapNodeID>> deferredReads;
deferredReads.reserve (maxDefer + 16);
std::stack <std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>>
stack;
using StackEntry = std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>;
std::stack <StackEntry, std::vector<StackEntry>> stack;
// Traverse the map without blocking
SHAMapTreeNode *node = root.get ();
@@ -164,11 +162,11 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
{
uint256 const& childHash = node->getChildHash (branch);
if (! m_fullBelowCache.touch_if_exists (childHash))
if (! mBacked || ! m_fullBelowCache.touch_if_exists (childHash))
{
SHAMapNodeID childID = nodeID.getChildNodeID (branch);
bool pending = false;
SHAMapTreeNode* d = getNodeAsync (childID, childHash, filter, pending);
SHAMapTreeNode* d = descendAsync (node, branch, childID, filter, pending);
if (!d)
{
@@ -186,7 +184,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
else
{
// read is deferred
deferredReads.emplace_back (childID, childHash);
deferredReads.emplace_back (node, branch, childID);
}
fullBelow = false; // This node is not known full below
@@ -212,7 +210,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
if (fullBelow)
{ // No partial node encountered below this node
node->setFullBelow ();
if (mType == smtSTATE)
if (mBacked)
m_fullBelowCache.insert (node->getNodeHash ());
}
@@ -238,10 +236,19 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
// Process all deferred reads
for (auto const& node : deferredReads)
{
auto const& nodeID = node.first;
auto const& nodeHash = node.second;
SHAMapTreeNode* nodePtr = getNodePointerNT (nodeID, nodeHash, filter);
if (!nodePtr && missingHashes.insert (nodeHash).second)
auto parent = std::get<0>(node);
auto branch = std::get<1>(node);
auto const& nodeID = std::get<2>(node);
auto const& nodeHash = parent->getChildHash (branch);
SHAMapTreeNode::pointer nodePtr = fetchNodeNT (nodeID, nodeHash, filter);
if (nodePtr)
{
if (mBacked)
canonicalize (nodeHash, nodePtr);
parent->canonicalizeChild (branch, nodePtr);
}
else if (missingHashes.insert (nodeHash).second)
{
nodeIDs.push_back (nodeID);
hashes.push_back (nodeHash);
@@ -273,11 +280,19 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
std::list<Blob >& rawNodes, bool fatRoot, bool fatLeaves)
{
// Gets a node and some of its children
ScopedReadLockType sl (mLock);
SHAMapTreeNode* node = getNodePointer(wanted);
SHAMapTreeNode* node = root.get ();
if (!node)
SHAMapNodeID nodeID;
while (node && node->isInner () && (nodeID.getDepth() < wanted.getDepth()))
{
int branch = nodeID.selectBranch (wanted.getNodeID());
node = descendThrow (node, branch);
nodeID = nodeID.getChildNodeID (branch);
}
if (!node || (nodeID != wanted))
{
WriteLog (lsWARNING, SHAMap) << "peer requested node that is not in the map: " << wanted;
throw std::runtime_error ("Peer requested node not in map");
@@ -300,8 +315,8 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
{
Serializer s;
node->addRaw (s, snfWIRE);
nodeIDs.push_back(wanted);
rawNodes.push_back (s.peekData ());
nodeIDs.push_back (wanted);
rawNodes.push_back (std::move (s.peekData ()));
}
if ((!fatRoot && wanted.isRoot ()) || node->isLeaf ()) // don't get a fat root, can't get a fat leaf
@@ -313,23 +328,22 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
count = 0;
for (int i = 0; i < 16; ++i)
{
SHAMapNodeID tempNodeID = wanted;
uint256 nextNodeHash;
if (node->descend (i, tempNodeID, nextNodeHash))
if (!node->isEmptyBranch (i))
{
nextNodeID = tempNodeID;
nextNode = getNodePointer (nextNodeID, nextNodeHash);
SHAMapNodeID nextNodeID = wanted.getChildNodeID (i);
nextNode = descendThrow (node, i);
++count;
if (fatLeaves || nextNode->isInner ())
{
Serializer s;
nextNode->addRaw (s, snfWIRE);
nodeIDs.push_back (nextNodeID);
rawNodes.push_back (s.peekData ());
rawNodes.push_back (std::move (s.peekData ()));
skipNode = true; // Don't add this node again if we loop
}
}
}
node = nextNode;
wanted = nextNodeID;
@@ -341,7 +355,6 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
{
ScopedReadLockType sl (mLock);
root->addRaw (s, format);
return true;
}
@@ -349,8 +362,6 @@ bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter)
{
ScopedWriteLockType sl (mLock);
// we already have a root node
if (root->getNodeHash ().isNonZero ())
{
@@ -360,7 +371,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
assert (mSeq >= 1);
SHAMapTreeNode::pointer node =
std::make_shared<SHAMapTreeNode> (rootNode, mSeq - 1,
std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false);
if (!node)
@@ -370,8 +381,10 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
node->dump (SHAMapNodeID ());
#endif
if (mBacked)
canonicalize (node->getNodeHash (), node);
root = node;
mTNByID.replace(SHAMapNodeID{}, root);
if (root->isLeaf())
clearSynching ();
@@ -390,8 +403,6 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter)
{
ScopedWriteLockType sl (mLock);
// we already have a root node
if (root->getNodeHash ().isNonZero ())
{
@@ -402,14 +413,16 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
assert (mSeq >= 1);
SHAMapTreeNode::pointer node =
std::make_shared<SHAMapTreeNode> (rootNode, mSeq - 1,
std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false);
if (!node || node->getNodeHash () != hash)
return SHAMapAddNode::invalid ();
if (mBacked)
canonicalize (hash, node);
root = node;
mTNByID.replace(SHAMapNodeID{}, root);
if (root->isLeaf())
clearSynching ();
@@ -429,8 +442,6 @@ SHAMapAddNode
SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
SHAMapSyncFilter* filter)
{
ScopedWriteLockType sl (mLock);
// return value: true=okay, false=error
assert (!node.isRoot ());
@@ -440,18 +451,10 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
return SHAMapAddNode::duplicate ();
}
if (checkCacheNode (node)) // Do we already have this node?
return SHAMapAddNode::duplicate ();
SHAMapNodeID iNodeID;
SHAMapTreeNode* iNode = root.get ();
SHAMapNodeID iNodeID = node.getParentNodeID();
SHAMapTreeNode* iNode = checkCacheNode(iNodeID).get();
if (iNode == nullptr)
{
iNode = root.get ();
iNodeID = SHAMapNodeID{};
}
while (!iNode->isLeaf () && !iNode->isFullBelow () &&
while (iNode->isInner () && !iNode->isFullBelow () &&
(iNodeID.getDepth () < node.getDepth ()))
{
int branch = iNodeID.selectBranch (node.getNodeID ());
@@ -467,12 +470,13 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
uint256 childHash = iNode->getChildHash (branch);
if (m_fullBelowCache.touch_if_exists (childHash))
return SHAMapAddNode::duplicate ();
SHAMapNodeID nextNodeID = iNodeID.getChildNodeID (branch);
SHAMapTreeNode* nextNode = getNodePointerNT(nextNodeID, childHash,
filter);
if (!nextNode)
SHAMapTreeNode* prevNode = iNode;
std::tie (iNode, iNodeID) = descend (iNode, iNodeID, branch, filter);
if (!iNode)
{
if (iNodeID.getDepth () != (node.getDepth () - 1))
if (iNodeID != node)
{
// Either this node is broken or we didn't request it (yet)
WriteLog (lsWARNING, SHAMap) << "unable to hook node " << node;
@@ -487,22 +491,25 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
std::make_shared<SHAMapTreeNode> (rawNode, 0, snfWIRE,
uZero, false);
if (childHash != newNode->getNodeHash ())
{
WriteLog (lsWARNING, SHAMap) << "Corrupt node received";
return SHAMapAddNode::invalid ();
}
canonicalize (childHash, newNode);
if (!iNode->isInBounds (iNodeID))
if (!newNode->isInBounds (iNodeID))
{
// Map is provably invalid
mState = smsInvalid;
return SHAMapAddNode::useful ();
}
if (mTNByID.canonicalize(node, &newNode) && filter)
if (childHash != newNode->getNodeHash ())
{
WriteLog (lsWARNING, SHAMap) << "Corrupt node received";
return SHAMapAddNode::invalid ();
}
if (mBacked)
canonicalize (childHash, newNode);
prevNode->canonicalizeChild (branch, newNode);
if (filter)
{
Serializer s;
newNode->addRaw (s, snfPREFIX);
@@ -512,8 +519,6 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
return SHAMapAddNode::useful ();
}
iNode = nextNode;
iNodeID = nextNodeID;
}
WriteLog (lsTRACE, SHAMap) << "got node, already had it (late)";
@@ -523,41 +528,29 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
bool SHAMap::deepCompare (SHAMap& other)
{
// Intended for debug/test only
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack;
ScopedReadLockType sl (mLock);
std::stack <std::pair <SHAMapTreeNode*, SHAMapTreeNode*> > stack;
stack.push ({root, SHAMapNodeID{}});
stack.push ({root.get(), other.root.get()});
while (!stack.empty ())
{
SHAMapTreeNode::pointer node;
SHAMapNodeID nodeID;
std::tie(node, nodeID) = stack.top ();
SHAMapTreeNode *node, *otherNode;
std::tie(node, otherNode) = stack.top ();
stack.pop ();
SHAMapTreeNode::pointer otherNode;
if (nodeID.isRoot ())
otherNode = other.root;
else
otherNode = other.getNode (nodeID, node->getNodeHash (), false);
if (!otherNode)
if (!node || !otherNode)
{
WriteLog (lsINFO, SHAMap) << "unable to fetch node";
return false;
}
else if (otherNode->getNodeHash () != node->getNodeHash ())
{
WriteLog (lsWARNING, SHAMap) << "node hash mismatch " << nodeID;
WriteLog (lsWARNING, SHAMap) << "node hash mismatch";
return false;
}
// WriteLog (lsTRACE) << "Comparing inner nodes " << *node;
if (node->getNodeHash () != otherNode->getNodeHash ())
return false;
if (node->isLeaf ())
{
if (!otherNode->isLeaf ())
@@ -583,16 +576,17 @@ bool SHAMap::deepCompare (SHAMap& other)
}
else
{
SHAMapNodeID nextNodeID = nodeID;
uint256 nextNodeHash;
if (!node->descend (i, nextNodeID, nextNodeHash))
if (otherNode->isEmptyBranch (i))
return false;
SHAMapTreeNode *next = descend (node, i);
SHAMapTreeNode *otherNext = other.descend (otherNode, i);
if (!next || !otherNext)
{
WriteLog (lsWARNING, SHAMap) << "unable to fetch inner node";
return false;
}
SHAMapTreeNode::pointer next = getNode (nextNodeID,
nextNodeHash, false);
stack.push ({next, nextNodeID});
stack.push ({next, otherNext});
}
}
}
@@ -602,49 +596,50 @@ bool SHAMap::deepCompare (SHAMap& other)
}
/** Does this map have this inner node?
You must hold a read lock to call this function
*/
bool
SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
uint256 const& targetNodeHash)
{
SHAMapTreeNode::pointer ptr = mTNByID.retrieve (targetNodeID);
if (ptr)
return ptr->getNodeHash() == targetNodeHash;
SHAMapTreeNode* node = root.get ();
SHAMapNodeID nodeID;
uint256 nodeHash;
while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ()))
{
int branch = nodeID.selectBranch (targetNodeID.getNodeID ());
if (!node->descend (branch, nodeID, nodeHash))
if (node->isEmptyBranch (branch))
return false;
node = getNodePointer (nodeID, nodeHash);
node = descendThrow (node, branch);
nodeID = nodeID.getChildNodeID (branch);
}
return nodeHash == targetNodeHash;
return (node->isInner()) && (node->getNodeHash() == targetNodeHash);
}
/** 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& targetNodeHash)
{
SHAMapTreeNode* node = root.get ();
SHAMapNodeID nodeID;
uint256 nodeHash;
if (!node->isInner()) // only one leaf node in the tree
return node->getNodeHash() == targetNodeHash;
do
{
int branch = nodeID.selectBranch (tag);
if (!node->descend (branch, nodeID, nodeHash))
if (node->isEmptyBranch (branch))
return false; // Dead end, node must not be here
if (nodeHash == targetNodeHash) // Matching leaf, no need to retrieve it
if (node->getChildHash (branch) == targetNodeHash) // Matching leaf, no need to retrieve it
return true;
node = getNodePointer (nodeID, nodeHash);
node = descendThrow (node, branch);
nodeID = nodeID.getChildNodeID (branch);
}
while (node->isInner());
@@ -663,18 +658,6 @@ There's no point in including the leaves of transaction trees.
void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
std::function<void (uint256 const&, const Blob&)> func)
{
ScopedReadLockType ul1 (mLock, boost::defer_lock);
ScopedReadLockType ul2;
if (have)
{
assert(this != have);
ul2 = ScopedReadLockType (have->mLock, boost::defer_lock);
std::lock(ul1, ul2);
}
else
ul1.lock();
if (root->getNodeHash ().isZero ())
return;
@@ -695,14 +678,16 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
return;
}
// contains unexplored non-matching inner node entries
std::stack<std::pair<SHAMapTreeNode*, SHAMapNodeID>> stack;
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapNodeID>;
std::stack <StackEntry, std::vector<StackEntry>> stack;
stack.push ({root.get(), SHAMapNodeID{}});
while (!stack.empty() && (max > 0))
{
SHAMapTreeNode* node;
SHAMapNodeID nodeID;
std::tie(node, nodeID) = stack.top ();
std::tie (node, nodeID) = stack.top ();
stack.pop ();
// 1) Add this node to the pack
@@ -718,8 +703,7 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
{
uint256 const& childHash = node->getChildHash (i);
SHAMapNodeID childID = nodeID.getChildNodeID (i);
SHAMapTreeNode* next = getNodePointer (childID, childHash);
SHAMapTreeNode* next = descendThrow (node, i);
if (next->isInner ())
{
@@ -738,12 +722,9 @@ 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)
{
ScopedReadLockType sl (mLock);
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack =
getStack (index, false);
auto stack = getStack (index, false);
if (stack.empty () || !stack.top ().first->isLeaf ())
throw std::runtime_error ("requested leaf not present");
@@ -796,6 +777,7 @@ public:
{
log <<
"Unable to add item to map";
assert (false);
return false;
}
}
@@ -806,6 +788,7 @@ public:
{
log <<
"Unable to remove item from map";
assert (false);
return false;
}
}
@@ -813,7 +796,8 @@ public:
if (beforeHash != map.getHash ())
{
log <<
"Hashes do not match";
"Hashes do not match " << beforeHash << " " << map.getHash ();
assert (false);
return false;
}

View File

@@ -21,10 +21,10 @@
namespace ripple {
std::mutex SHAMapTreeNode::childLock;
SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq)
: mHash (std::uint64_t(0))
, mSeq (seq)
, mAccessSeq (seq)
: mSeq (seq)
, mType (tnERROR)
, mIsBranch (0)
, mFullBelow (false)
@@ -41,7 +41,14 @@ SHAMapTreeNode::SHAMapTreeNode (const SHAMapTreeNode& node, std::uint32_t seq)
if (node.mItem)
mItem = node.mItem;
else
{
memcpy (mHashes, node.mHashes, sizeof (mHashes));
std::unique_lock <std::mutex> lock (childLock);
for (int i = 0; i < 16; ++i)
mChildren[i] = node.mChildren[i];
}
}
SHAMapTreeNode::SHAMapTreeNode (SHAMapItem::ref item,
@@ -471,11 +478,13 @@ std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const
return ret;
}
bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash)
// We are modifying an inner node
bool SHAMapTreeNode::setChild (int m, uint256 const& hash, SHAMapTreeNode::ref child)
{
assert ((m >= 0) && (m < 16));
assert (mType == tnINNER);
assert (mSeq != 0);
assert (child.get() != this);
if (mHashes[m] == hash)
return false;
@@ -483,31 +492,72 @@ bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash)
mHashes[m] = hash;
if (hash.isNonZero ())
{
assert (child && (child->getNodeHash() == hash));
mIsBranch |= (1 << m);
}
else
{
assert (!child);
mIsBranch &= ~ (1 << m);
}
mChildren[m] = child;
return updateHash ();
}
// Descends along the specified branch
// On invocation, nodeID must be the ID of this node
// Returns false if there is no node down that branch
// Otherwise, returns true and fills in the node's ID and hash
// finished modifying, now make shareable
void SHAMapTreeNode::shareChild (int m, SHAMapTreeNode::ref child)
{
assert ((m >= 0) && (m < 16));
assert (mType == tnINNER);
assert (mSeq != 0);
assert (child);
assert (child.get() != this);
assert (child->getNodeHash() == mHashes[m]);
bool
SHAMapTreeNode::descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash)
mChildren[m] = child;
}
SHAMapTreeNode* SHAMapTreeNode::getChildPointer (int branch)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
if (isEmptyBranch (branch))
return false;
nodeID = nodeID.getChildNodeID (branch);
nodeHash = mHashes [branch];
return true;
std::unique_lock <std::mutex> lock (childLock);
return mChildren[branch].get ();
}
SHAMapTreeNode::pointer SHAMapTreeNode::getChild (int branch)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
std::unique_lock <std::mutex> lock (childLock);
assert (!mChildren[branch] || (mHashes[branch] == mChildren[branch]->getNodeHash()));
return mChildren[branch];
}
void SHAMapTreeNode::canonicalizeChild (int branch, SHAMapTreeNode::pointer& node)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
assert (node);
assert (node->getNodeHash() == mHashes[branch]);
std::unique_lock <std::mutex> lock (childLock);
if (mChildren[branch])
{
// There is already a node hooked up, return it
node = mChildren[branch];
}
else
{
// Hook this node up
mChildren[branch] = node;
}
}
} // ripple

View File

@@ -77,12 +77,7 @@ public:
}
void setSeq (std::uint32_t s)
{
mAccessSeq = mSeq = s;
}
void touch (std::uint32_t s)
{
if (mSeq != 0)
mAccessSeq = s;
mSeq = s;
}
uint256 const& getNodeHash () const
{
@@ -130,7 +125,13 @@ public:
{
return !mItem;
}
bool setChildHash (int m, uint256 const& hash);
// We are modifying the child hash
bool setChild (int m, uint256 const& hash, std::shared_ptr<SHAMapTreeNode> const& child);
// We are sharing/unsharing the child
void shareChild (int m, std::shared_ptr<SHAMapTreeNode> const& child);
bool isEmptyBranch (int m) const
{
return (mIsBranch & (1 << m)) == 0;
@@ -178,32 +179,27 @@ public:
virtual void dump (SHAMapNodeID const&);
virtual std::string getString (SHAMapNodeID const&) const;
/** Descend along the specified branch
On invocation, nodeID must be the ID of this node
Returns `false` if there is no node down that branch
Otherwise, returns `true` and fills in the node's ID and hash
@param branch The branch to descend [0, 15]
@param nodeID On entry the ID of the parent. On exit the ID of the child
@param nodeHash On exit the hash of the child node.
@return `true` if nodeID and nodeHash are altered.
*/
bool descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash);
SHAMapTreeNode* getChildPointer (int branch);
SHAMapTreeNode::pointer getChild (int branch);
void canonicalizeChild (int branch, SHAMapTreeNode::pointer& node);
private:
// VFALCO TODO remove the use of friend
friend class SHAMap;
uint256 mHash;
uint256 mHashes[16];
SHAMapItem::pointer mItem;
std::uint32_t mSeq, mAccessSeq;
TNType mType;
int mIsBranch;
bool mFullBelow;
uint256 mHash;
uint256 mHashes[16];
SHAMapTreeNode::pointer mChildren[16];
SHAMapItem::pointer mItem;
std::uint32_t mSeq;
TNType mType;
int mIsBranch;
bool mFullBelow;
bool updateHash ();
static std::mutex childLock;
};
using TreeNodeCache = TaggedCache <uint256, SHAMapTreeNode>;

View File

@@ -37,7 +37,7 @@ TransactionAcquire::TransactionAcquire (uint256 const& hash, clock_type& clock)
Application& app = getApp();
mMap = std::make_shared<SHAMap> (smtTRANSACTION, hash,
app.getFullBelowCache (), app.getTreeNodeCache());
mMap->setTXMap ();
mMap->setUnbacked ();
}
TransactionAcquire::~TransactionAcquire ()