From fc560179e09dcca5f9b10943eaa1e4720e53e900 Mon Sep 17 00:00:00 2001 From: David Schwartz Date: Thu, 31 Jul 2014 16:38:58 -0700 Subject: [PATCH] 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 --- src/ripple/app/consensus/LedgerConsensus.cpp | 28 +- src/ripple/app/ledger/InboundLedger.cpp | 3 - src/ripple/app/ledger/Ledger.cpp | 15 +- src/ripple/app/ledger/Ledger.h | 10 - src/ripple/app/ledger/LedgerCleaner.cpp | 2 - src/ripple/app/shamap/SHAMap.cpp | 1203 ++++++++---------- src/ripple/app/shamap/SHAMap.h | 140 +- src/ripple/app/shamap/SHAMapDelta.cpp | 117 +- src/ripple/app/shamap/SHAMapMissingNode.cpp | 6 +- src/ripple/app/shamap/SHAMapMissingNode.h | 8 - src/ripple/app/shamap/SHAMapNodeID.cpp | 1 + src/ripple/app/shamap/SHAMapSync.cpp | 288 ++--- src/ripple/app/shamap/SHAMapTreeNode.cpp | 84 +- src/ripple/app/shamap/SHAMapTreeNode.h | 46 +- src/ripple/app/tx/TransactionAcquire.cpp | 2 +- 15 files changed, 889 insertions(+), 1064 deletions(-) diff --git a/src/ripple/app/consensus/LedgerConsensus.cpp b/src/ripple/app/consensus/LedgerConsensus.cpp index 6ea4d38a3..132ea2f4d 100644 --- a/src/ripple/app/consensus/LedgerConsensus.cpp +++ b/src/ripple/app/consensus/LedgerConsensus.cpp @@ -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 acctNodes - = newLCL->peekAccountStateMap ()->disarmDirty (); // stop tracking changes - std::shared_ptr 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); diff --git a/src/ripple/app/ledger/InboundLedger.cpp b/src/ripple/app/ledger/InboundLedger.cpp index d8553168a..fa64eb01b 100644 --- a/src/ripple/app/ledger/InboundLedger.cpp +++ b/src/ripple/app/ledger/InboundLedger.cpp @@ -1232,9 +1232,6 @@ Json::Value InboundLedger::getJson (int) { ret["have_state"] = mHaveState; ret["have_transactions"] = mHaveTransactions; - if (!mHaveState) - ret["state_nodes"] = static_cast - (mLedger->peekAccountStateMap()->size()); } if (mAborted) diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index c01e15b28..9d682f589 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -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 (mTransactionMap, - "mTransactionMap with " + - std::to_string(mTransactionMap->size ()) + " items"); + logTimedDestroy (mTransactionMap, "mTransactionMap"); } if (mAccountStateMap) { - logTimedDestroy (mAccountStateMap, - "mAccountStateMap with " + - std::to_string (mAccountStateMap->size ()) + " items"); + logTimedDestroy (mAccountStateMap, "mAccountStateMap"); } } diff --git a/src/ripple/app/ledger/Ledger.h b/src/ripple/app/ledger/Ledger.h index a8d0bfb08..25151af73 100644 --- a/src/ripple/app/ledger/Ledger.h +++ b/src/ripple/app/ledger/Ledger.h @@ -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); diff --git a/src/ripple/app/ledger/LedgerCleaner.cpp b/src/ripple/app/ledger/LedgerCleaner.cpp index a4cd5518e..56d43b15a 100644 --- a/src/ripple/app/ledger/LedgerCleaner.cpp +++ b/src/ripple/app/ledger/LedgerCleaner.cpp @@ -305,8 +305,6 @@ public: return false; } - nodeLedger->dropCache(); - return true; } diff --git a/src/ripple/app/shamap/SHAMap.cpp b/src/ripple/app/shamap/SHAMap.cpp index 6a4924867..c38ec7f21 100644 --- a/src/ripple/app/shamap/SHAMap.cpp +++ b/src/ripple/app/shamap/SHAMap.cpp @@ -42,17 +42,13 @@ SHAMap::SHAMap ( , mTreeNodeCache (treeNodeCache) , mState (smsModifying) , mType (t) - , mTXMap (false) + , mBacked (true) , m_missing_node_handler (missing_node_handler) { assert (mSeq != 0); - if (t == smtSTATE) - mTNByID.rehash (STATE_MAP_BUCKETS); - SHAMapNodeID rootID{}; root = std::make_shared (mSeq); root->makeInner (); - mTNByID.replace(rootID, root); } SHAMap::SHAMap ( @@ -67,31 +63,17 @@ SHAMap::SHAMap ( , mTreeNodeCache (treeNodeCache) , mState (smsSynching) , mType (t) - , mTXMap (false) + , mBacked (true) , m_missing_node_handler (missing_node_handler) { - if (t == smtSTATE) - mTNByID.rehash (STATE_MAP_BUCKETS); - - SHAMapNodeID rootID{}; root = std::make_shared (mSeq); root->makeInner (); - mTNByID.replace(rootID, root); } SHAMap::~SHAMap () { mState = smsInvalid; - logTimedDestroy (mTNByID, "mTNByID with " + - std::to_string (mTNByID.size ()) + " items"); - - if (mDirtyNodes) - { - logTimedDestroy (mDirtyNodes, "mDirtyNodes with " + - std::to_string (mDirtyNodes->size ()) + " items"); - } - if (root) logTimedDestroy (root, "root node"); } @@ -102,48 +84,30 @@ SHAMap::pointer SHAMap::snapShot (bool isMutable) m_fullBelowCache, mTreeNodeCache); SHAMap& newMap = *ret; - // Return a new SHAMap that is a snapshot of this one - // Initially most nodes are shared and CoW is forced where needed + if (!isMutable) + newMap.mState = smsImmutable; + + newMap.mSeq = mSeq + 1; + newMap.root = root; + + if ((mState != smsImmutable) || !isMutable) { - ScopedReadLockType sl (mLock); - newMap.mSeq = mSeq; - newMap.mTNByID = mTNByID; - newMap.root = root; - - if (!isMutable) - newMap.mState = smsImmutable; - - // If the existing map has any nodes it might modify, unshare ours now - if (mState != smsImmutable) - { - for (NodeMap::value_type& nodeIt : mTNByID.peekMap()) - { - if (nodeIt.second->getSeq() == mSeq) - { // We might modify this node, so duplicate it in the snapShot - SHAMapTreeNode::pointer newNode = std::make_shared (*nodeIt.second, mSeq); - SHAMapNodeID const& newNodeID = nodeIt.first; - newMap.mTNByID.replace (newNodeID, newNode); - if (newNodeID.isRoot ()) - newMap.root = newNode; - } - } - } - else if (isMutable) // Need to unshare on changes to the snapshot - ++newMap.mSeq; + // If either map may change, they cannot share nodes + newMap.unshare (); } return ret; } -std::stack> +SHAMap::SharedPtrNodeStack SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) { // Walk the tree as far as possible to the specified identifier // produce a stack of nodes along the way, with the terminal node at the top - std::stack> stack; + SharedPtrNodeStack stack; + SHAMapTreeNode::pointer node = root; SHAMapNodeID nodeID; - uint256 nodeHash; while (!node->isLeaf ()) { @@ -152,10 +116,11 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) int branch = nodeID.selectBranch (id); assert (branch >= 0); - if (!node->descend (branch, nodeID, nodeHash)) + if (node->isEmptyBranch (branch)) return stack; - node = getNode (nodeID, nodeHash, false); + node = descendThrow (node, branch); + nodeID = nodeID.getChildNodeID (branch); } if (include_nonmatching_leaf || (node->peekItem ()->getTag () == id)) @@ -165,13 +130,16 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) } void -SHAMap::dirtyUp (std::stack>& stack, - uint256 const& target, uint256 prevHash) +SHAMap::dirtyUp (SharedPtrNodeStack& stack, + uint256 const& target, SHAMapTreeNode::pointer child) { // walk the tree up from through the inner nodes to the root - // update linking hashes and add nodes to dirty list + // update hashes and links + // stack is a path of inner nodes up to, but not including, child + // child can be an inner node or a leaf assert ((mState != smsSynching) && (mState != smsImmutable)); + assert (child && (child->getSeq() == mSeq)); while (!stack.empty ()) { @@ -183,9 +151,9 @@ SHAMap::dirtyUp (std::stack>& s int branch = nodeID.selectBranch (target); assert (branch >= 0); - returnNode (node, nodeID, true); + unshareNode (node, nodeID); - if (!node->setChildHash (branch, prevHash)) + if (! node->setChild (branch, child->getNodeHash(), child)) { WriteLog (lsFATAL, SHAMap) << "dirtyUp terminates early"; assert (false); @@ -195,200 +163,295 @@ SHAMap::dirtyUp (std::stack>& s #ifdef ST_DEBUG WriteLog (lsTRACE, SHAMap) << "dirtyUp sets branch " << branch << " to " << prevHash; #endif - prevHash = node->getNodeHash (); - assert (prevHash.isNonZero ()); + child = std::move (node); } } -SHAMapTreeNode::pointer SHAMap::checkCacheNode (const SHAMapNodeID& iNode) -{ - SHAMapTreeNode::pointer ret = mTNByID.retrieve(iNode); - if (ret && (ret->getSeq()!= 0)) - ret->touch (mSeq); - return ret; -} - -SHAMapTreeNode::pointer SHAMap::walkTo (uint256 const& id, bool modify) -{ - // walk down to the terminal node for this ID - - SHAMapTreeNode::pointer inNode = root; - SHAMapNodeID nodeID; - uint256 nodeHash; - - while (!inNode->isLeaf ()) - { - int branch = nodeID.selectBranch (id); - - if (!inNode->descend (branch, nodeID, nodeHash)) - return inNode; - - inNode = getNode (nodeID, nodeHash, false); - } - - if (inNode->getTag () != id) - return SHAMapTreeNode::pointer (); - - if (modify) - returnNode (inNode, nodeID, true); - - return inNode; -} - SHAMapTreeNode* SHAMap::walkToPointer (uint256 const& id) { SHAMapTreeNode* inNode = root.get (); SHAMapNodeID nodeID; uint256 nodeHash; - while (!inNode->isLeaf ()) + while (inNode->isInner ()) { int branch = nodeID.selectBranch (id); - if (!inNode->descend (branch, nodeID, nodeHash)) + if (inNode->isEmptyBranch (branch)) return nullptr; - inNode = getNodePointer (nodeID, nodeHash); - assert (inNode); + inNode = descendThrow (inNode, branch); + nodeID = nodeID.getChildNodeID (branch); } return (inNode->getTag () == id) ? inNode : nullptr; } -SHAMapTreeNode::pointer SHAMap::getNode (const SHAMapNodeID& id, uint256 const& hash, bool modify) +SHAMapTreeNode::pointer SHAMap::fetchNodeFromDB (uint256 const& hash) { - // retrieve a node whose node hash is known - SHAMapTreeNode::pointer node = checkCacheNode (id); + SHAMapTreeNode::pointer node; - if (node) + if (mBacked && getApp().running ()) { -#if BEAST_DEBUG - - if (node->getNodeHash () != hash) + NodeObject::pointer obj = getApp().getNodeStore().fetch (hash); + if (obj) { - WriteLog (lsFATAL, SHAMap) << "Attempt to get node, hash not in tree"; - WriteLog (lsFATAL, SHAMap) << "ID: " << id; - WriteLog (lsFATAL, SHAMap) << "TgtHash " << hash; - WriteLog (lsFATAL, SHAMap) << "NodHash " << node->getNodeHash (); - throw std::runtime_error ("invalid node"); + try + { + node = std::make_shared (obj->getData(), + 0, snfPREFIX, hash, true); + canonicalize (hash, node); + } + catch (...) + { + WriteLog (lsWARNING, SHAMap) << "Invalid DB node " << hash; + return SHAMapTreeNode::pointer (); + } } - -#endif - returnNode (node, id, modify); - return node; - } - - return fetchNodeExternal (id, hash); -} - -SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNodeID& id, uint256 const& hash) -{ - // fast, but you do not hold a reference - SHAMapTreeNode* ret = getNodePointerNT (id, hash); - - if (!ret) - throw (SHAMapMissingNode (mType, id, hash)); - - return ret; -} - -SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNodeID& id, uint256 const& hash) -{ - SHAMapTreeNode::pointer ret = mTNByID.retrieve (id); - if (!ret) - ret = fetchNodeExternalNT (id, hash); - return ret ? ret.get() : nullptr; -} - -SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNodeID& id, uint256 const& hash, SHAMapSyncFilter* filter) -{ - SHAMapTreeNode* ret = getNodePointerNT (id, hash, filter); - - if (!ret) - throw (SHAMapMissingNode (mType, id, hash)); - - return ret; -} - -SHAMapTreeNode* SHAMap::getNodePointerNT (const SHAMapNodeID& id, uint256 const& hash, SHAMapSyncFilter* filter) -{ - SHAMapTreeNode* node = getNodePointerNT (id, hash); - - if (!node && filter) - { // Our regular node store didn't have the node. See if the filter does - Blob nodeData; - - if (filter->haveNode (id, hash, nodeData)) + else if (mLedgerSeq != 0) { - SHAMapTreeNode::pointer node = std::make_shared ( - nodeData, 0, snfPREFIX, hash, true); - canonicalize (hash, 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 ()); - - return node.get (); + m_missing_node_handler (mLedgerSeq); + mLedgerSeq = 0; } } return node; } +// See if a sync filter has a node +SHAMapTreeNode::pointer SHAMap::checkFilter ( + uint256 const& hash, + SHAMapNodeID const& id, + SHAMapSyncFilter* filter) +{ + SHAMapTreeNode::pointer node; + Blob nodeData; + + if (filter->haveNode (id, hash, nodeData)) + { + node = std::make_shared ( + nodeData, 0, snfPREFIX, hash, true); + + filter->gotNode (true, id, hash, nodeData, node->getType ()); + + if (mBacked) + canonicalize (hash, node); + } + + return node; +} + +// Get a node without throwing +// Used on maps where missing nodes are expected +SHAMapTreeNode::pointer SHAMap::fetchNodeNT( + SHAMapNodeID const& id, + uint256 const& hash, + SHAMapSyncFilter* filter) +{ + SHAMapTreeNode::pointer node = getCache (hash); + if (node) + return node; + + if (mBacked) + { + node = fetchNodeFromDB (hash); + if (node) + { + canonicalize (hash, node); + return node; + } + } + + if (filter) + node = checkFilter (hash, id, filter); + + return node; +} + +SHAMapTreeNode::pointer SHAMap::fetchNodeNT (uint256 const& hash) +{ + SHAMapTreeNode::pointer node = getCache (hash); + + if (!node && mBacked) + node = fetchNodeFromDB (hash); + + return node; +} + +// Throw if the node is missing +SHAMapTreeNode::pointer SHAMap::fetchNode (uint256 const& hash) +{ + SHAMapTreeNode::pointer node = fetchNodeNT (hash); + + if (!node) + throw SHAMapMissingNode (mType, hash); + + return node; +} + +SHAMapTreeNode* SHAMap::descendThrow (SHAMapTreeNode* parent, int branch) +{ + SHAMapTreeNode* ret = descend (parent, branch); + + if (! ret && ! parent->isEmptyBranch (branch)) + throw SHAMapMissingNode (mType, parent->getChildHash (branch)); + + return ret; +} + +SHAMapTreeNode::pointer SHAMap::descendThrow (SHAMapTreeNode::ref parent, int branch) +{ + SHAMapTreeNode::pointer ret = descend (parent, branch); + + if (! ret && ! parent->isEmptyBranch (branch)) + throw SHAMapMissingNode (mType, parent->getChildHash (branch)); + + return ret; +} + +SHAMapTreeNode* SHAMap::descend (SHAMapTreeNode* parent, int branch) +{ + SHAMapTreeNode* ret = parent->getChildPointer (branch); + if (ret || !mBacked) + return ret; + + SHAMapTreeNode::pointer node = fetchNodeNT (parent->getChildHash (branch)); + if (!node) + return nullptr; + + parent->canonicalizeChild (branch, node); + return node.get (); +} + +SHAMapTreeNode::pointer SHAMap::descend (SHAMapTreeNode::ref parent, int branch) +{ + SHAMapTreeNode::pointer node = parent->getChild (branch); + if (node || !mBacked) + return node; + + node = fetchNode (parent->getChildHash (branch)); + if (!node) + return nullptr; + + parent->canonicalizeChild (branch, node); + return node; +} + +// Gets the node that would be hooked to this branch, +// but doesn't hook it up. +SHAMapTreeNode::pointer SHAMap::descendNoStore (SHAMapTreeNode::ref parent, int branch) +{ + SHAMapTreeNode::pointer ret = parent->getChild (branch); + if (!ret && mBacked) + ret = fetchNode (parent->getChildHash (branch)); + return ret; +} + +std::pair +SHAMap::descend (SHAMapTreeNode * parent, SHAMapNodeID const& parentID, + int branch, SHAMapSyncFilter * filter) +{ + assert (parent->isInner ()); + assert ((branch >= 0) && (branch < 16)); + assert (!parent->isEmptyBranch (branch)); + + SHAMapNodeID childID = parentID.getChildNodeID (branch); + SHAMapTreeNode* child = parent->getChildPointer (branch); + uint256 const& childHash = parent->getChildHash (branch); + + if (!child) + { + SHAMapTreeNode::pointer childNode = fetchNodeNT (childID, childHash, filter); + + if (childNode) + { + parent->canonicalizeChild (branch, childNode); + child = childNode.get (); + } + } + + return std::make_pair (child, childID); +} + +SHAMapTreeNode* SHAMap::descendAsync (SHAMapTreeNode* parent, int branch, + SHAMapNodeID const& childID, SHAMapSyncFilter * filter, bool & pending) +{ + pending = false; + + SHAMapTreeNode* ret = parent->getChildPointer (branch); + if (ret) + return ret; + + uint256 const& hash = parent->getChildHash (branch); + + SHAMapTreeNode::pointer ptr = getCache (hash); + if (!ptr) + { + if (filter) + ptr = checkFilter (hash, childID, filter); + + if (!ptr && mBacked) + { + NodeObject::pointer obj; + if (!getApp().getNodeStore().asyncFetch (hash, obj)) + { + pending = true; + return nullptr; + } + if (!obj) + return nullptr; + + ptr = std::make_shared (obj->getData(), 0, snfPREFIX, hash, true); + + if (mBacked) + canonicalize (hash, ptr); + } + } + + if (ptr) + parent->canonicalizeChild (branch, ptr); + + return ptr.get (); +} void -SHAMap::returnNode (SHAMapTreeNode::pointer& node, SHAMapNodeID const& nodeID, - bool modify) +SHAMap::unshareNode (SHAMapTreeNode::pointer& node, SHAMapNodeID const& nodeID) { // make sure the node is suitable for the intended operation (copy on write) assert (node->isValid ()); assert (node->getSeq () <= mSeq); - if (node && modify && (node->getSeq () != mSeq)) + if (node->getSeq () != mSeq) { // have a CoW - assert (node->getSeq () < mSeq); assert (mState != smsImmutable); node = std::make_shared (*node, mSeq); // here's to the new node, same as the old node assert (node->isValid ()); - mTNByID.replace (nodeID, node); - if (nodeID.isRoot ()) root = node; - - if (mDirtyNodes) - mDirtyNodes->insert (nodeID); } } -void SHAMap::trackNewNode (SHAMapTreeNode::pointer& node, - SHAMapNodeID const& nodeID) -{ - assert (node->getSeq() == mSeq); - if (mDirtyNodes) - mDirtyNodes->insert (nodeID); -} - SHAMapTreeNode* -SHAMap::firstBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) +SHAMap::firstBelow (SHAMapTreeNode* node) { // Return the first item below this node do { assert(node != nullptr); - // Walk down the tree + if (node->hasItem ()) return node; + + // Walk down the tree bool foundNode = false; for (int i = 0; i < 16; ++i) { - uint256 nodeHash; - if (node->descend (i, nodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - node = getNodePointer (nodeID, nodeHash); + node = descendThrow (node, i); foundNode = true; break; } @@ -400,20 +463,20 @@ SHAMap::firstBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) } SHAMapTreeNode* -SHAMap::lastBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) +SHAMap::lastBelow (SHAMapTreeNode* node) { do { - // Walk down the tree if (node->hasItem ()) return node; + + // Walk down the tree bool foundNode = false; for (int i = 15; i >= 0; --i) { - uint256 nodeHash; - if (node->descend (i, nodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - node = getNodePointer (nodeID, nodeHash); + node = descendThrow (node, i); foundNode = true; break; } @@ -425,86 +488,45 @@ SHAMap::lastBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) } SHAMapItem::pointer -SHAMap::onlyBelow (SHAMapTreeNode* node, SHAMapNodeID nodeID) +SHAMap::onlyBelow (SHAMapTreeNode* node) { // If there is only one item below this node, return it + while (!node->isLeaf ()) { SHAMapTreeNode* nextNode = nullptr; - SHAMapNodeID nextNodeID; for (int i = 0; i < 16; ++i) { - SHAMapNodeID tempNodeID = nodeID; - uint256 nodeHash; - if (node->descend (i, tempNodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { if (nextNode) - return SHAMapItem::pointer (); // two leaves below - nextNode = getNodePointer (tempNodeID, nodeHash); - nextNodeID = tempNodeID; + return SHAMapItem::pointer (); + + nextNode = descendThrow (node, i); } } + if (!nextNode) { - WriteLog (lsFATAL, SHAMap) << nodeID; assert (false); return SHAMapItem::pointer (); } node = nextNode; - nodeID = nextNodeID; } - assert (node->hasItem ()); + // An inner node must have at least one leaf + // below it, unless it's the root + assert (node->hasItem () || (node == root.get ())); + return node->peekItem (); } -void -SHAMap::eraseChildren (SHAMapTreeNode::pointer node, SHAMapNodeID nodeID) -{ - // this node has only one item below it, erase its children - bool erase = false; - while (node->isInner ()) - { - for (int i = 0; i < 16; ++i) - { - uint256 nodeHash; - SHAMapNodeID nextNodeID = nodeID; - if (node->descend (i, nextNodeID, nodeHash)) - { - SHAMapTreeNode::pointer nextNode = getNode (nextNodeID, - nodeHash, false); - if (erase) - { - returnNode (node, nodeID, true); - - if (mTNByID.erase (nodeID)) - assert (false); - } - - erase = true; - node = nextNode; - nodeID = nextNodeID; - break; - } - } - } - - returnNode (node, nodeID, true); - - if (mTNByID.erase (nodeID) == 0) - assert (false); - - return; -} - static const SHAMapItem::pointer no_item; SHAMapItem::pointer SHAMap::peekFirstItem () { - ScopedReadLockType sl (mLock); - - SHAMapTreeNode* node = firstBelow (root.get (), SHAMapNodeID{}); + SHAMapTreeNode* node = firstBelow (root.get ()); if (!node) return no_item; @@ -514,9 +536,7 @@ SHAMapItem::pointer SHAMap::peekFirstItem () SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type) { - ScopedReadLockType sl (mLock); - - SHAMapTreeNode* node = firstBelow (root.get (), SHAMapNodeID{}); + SHAMapTreeNode* node = firstBelow (root.get ()); if (!node) return no_item; @@ -527,9 +547,7 @@ SHAMapItem::pointer SHAMap::peekFirstItem (SHAMapTreeNode::TNType& type) SHAMapItem::pointer SHAMap::peekLastItem () { - ScopedReadLockType sl (mLock); - - SHAMapTreeNode* node = lastBelow (root.get (), SHAMapNodeID{}); + SHAMapTreeNode* node = lastBelow (root.get ()); if (!node) return no_item; @@ -543,18 +561,16 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id) return peekNextItem (id, 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 - ScopedReadLockType sl (mLock); - std::stack> stack = - getStack (id, true); + auto stack = getStack (id, true); + while (!stack.empty ()) { - SHAMapTreeNode::pointer node = stack.top ().first; - SHAMapNodeID nodeID = stack.top ().second; + SHAMapTreeNode* node = stack.top().first.get(); + SHAMapNodeID nodeID = stack.top().second; stack.pop (); if (node->isLeaf ()) @@ -567,25 +583,19 @@ SHAMapItem::pointer SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNT } else { - uint256 nodeHash; // breadth-first for (int i = nodeID.selectBranch (id) + 1; i < 16; ++i) - { - SHAMapNodeID childNodeID = nodeID; - if (node->descend (i, childNodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - SHAMapTreeNode* firstNode = getNodePointer ( - childNodeID, nodeHash); - assert (firstNode); - firstNode = firstBelow (firstNode, childNodeID); + node = descendThrow (node, i); + node = firstBelow (node); - if (!firstNode || firstNode->isInner ()) + if (!node || node->isInner ()) throw (std::runtime_error ("missing/corrupt node")); - type = firstNode->getType (); - return firstNode->peekItem (); + type = node->getType (); + return node->peekItem (); } - } } } @@ -596,13 +606,11 @@ 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 SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id) { - ScopedReadLockType sl (mLock); + auto stack = getStack (id, true); - std::stack> stack = - getStack (id, true); while (!stack.empty ()) { - SHAMapTreeNode::pointer node = stack.top ().first; + SHAMapTreeNode* node = stack.top ().first.get(); SHAMapNodeID nodeID = stack.top ().second; stack.pop (); @@ -613,31 +621,24 @@ SHAMapItem::pointer SHAMap::peekPrevItem (uint256 const& id) } else { - uint256 nodeHash; for (int i = nodeID.selectBranch (id) - 1; i >= 0; --i) { - if (node->descend (i, nodeID, nodeHash)) + if (!node->isEmptyBranch (i)) { - node = getNode (nodeID, nodeHash, false); - SHAMapTreeNode* item = firstBelow (node.get (), nodeID); - - if (!item) - throw (std::runtime_error ("missing node")); - - return item->peekItem (); + node = descendThrow (node, i); + node = lastBelow (node); + return node->peekItem (); } } } } - // must be last item + // must be first item return no_item; } SHAMapItem::pointer SHAMap::peekItem (uint256 const& id) { - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); if (!leaf) @@ -648,8 +649,6 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id) SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType& type) { - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); if (!leaf) @@ -661,8 +660,6 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, SHAMapTreeNode::TNType& SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash) { - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); if (!leaf) @@ -676,8 +673,6 @@ SHAMapItem::pointer SHAMap::peekItem (uint256 const& id, uint256& hash) bool SHAMap::hasItem (uint256 const& id) { // does the tree have an item with this ID - ScopedReadLockType sl (mLock); - SHAMapTreeNode* leaf = walkToPointer (id); return (leaf != nullptr); } @@ -685,11 +680,10 @@ bool SHAMap::hasItem (uint256 const& id) bool SHAMap::delItem (uint256 const& id) { // delete the item with this ID - ScopedWriteLockType sl (mLock); assert (mState != smsImmutable); - std::stack> stack = - getStack (id, true); + auto stack = getStack (id, true); + if (stack.empty ()) throw (std::runtime_error ("missing node")); @@ -701,21 +695,22 @@ bool SHAMap::delItem (uint256 const& id) return false; SHAMapTreeNode::TNType type = leaf->getType (); - returnNode (leaf, leafID, true); - - if (mTNByID.erase (leafID) == 0) - assert (false); + // What gets attached to the end of the chain + // (For now, nothing, since we deleted the leaf) uint256 prevHash; + SHAMapTreeNode::pointer prevNode; while (!stack.empty ()) { SHAMapTreeNode::pointer node = stack.top ().first; SHAMapNodeID nodeID = stack.top ().second; stack.pop (); - returnNode (node, nodeID, true); + assert (node->isInner ()); - if (!node->setChildHash (nodeID.selectBranch (id), prevHash)) + + unshareNode (node, nodeID); + if (! node->setChild (nodeID.selectBranch (id), prevHash, prevNode)) { assert (false); return true; @@ -724,37 +719,48 @@ bool SHAMap::delItem (uint256 const& id) if (!nodeID.isRoot ()) { // we may have made this a node with 1 or 0 children + // And, if so, we need to remove this branch int bc = node->getBranchCount (); if (bc == 0) { + // no children below this branch prevHash = uint256 (); - - if (!mTNByID.erase (nodeID)) - assert (false); + prevNode.reset (); } else if (bc == 1) { - // pull up on the thread - SHAMapItem::pointer item = onlyBelow (node.get (), nodeID); + // If there's only one item, pull up on the thread + SHAMapItem::pointer item = onlyBelow (node.get ()); if (item) { - returnNode (node, nodeID, true); - eraseChildren (node, nodeID); + for (int i = 0; i < 16; ++i) + { + if (!node->isEmptyBranch (i)) + { + if (! node->setChild (i, uint256(), nullptr)) + { + assert (false); + } + break; + } + } node->setItem (item, type); } prevHash = node->getNodeHash (); + prevNode = std::move (node); assert (prevHash.isNonZero ()); } else { + // This node is now the end of the branch prevHash = node->getNodeHash (); + prevNode = std::move (node); assert (prevHash.isNonZero ()); } } - else assert (stack.empty ()); } return true; @@ -765,13 +771,12 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta // add the specified item, does not update uint256 tag = item->getTag (); SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : - (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); + (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); - ScopedWriteLockType sl (mLock); assert (mState != smsImmutable); - std::stack> stack = - getStack (tag, true); + auto stack = getStack (tag, true); + if (stack.empty ()) throw (std::runtime_error ("missing node")); @@ -782,28 +787,18 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta if (node->isLeaf () && (node->peekItem ()->getTag () == tag)) return false; - uint256 prevHash; - returnNode (node, nodeID, true); + unshareNode (node, nodeID); if (node->isInner ()) { // easy case, we end on an inner node int branch = nodeID.selectBranch (tag); assert (node->isEmptyBranch (branch)); - SHAMapNodeID newNodeID = nodeID.getChildNodeID (branch); SHAMapTreeNode::pointer newNode = std::make_shared (item, type, mSeq); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) + if (! node->setChild (branch, newNode->getNodeHash (), newNode)) { - WriteLog (lsFATAL, SHAMap) << "Node: " << nodeID; - WriteLog (lsFATAL, SHAMap) << "NewNode: " << newNodeID; - dump (); assert (false); - throw (std::runtime_error ("invalid inner node")); } - - trackNewNode (newNode, newNodeID); - node->setChildHash (branch, newNode->getNodeHash ()); } else { @@ -818,45 +813,34 @@ bool SHAMap::addGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasMeta while ((b1 = nodeID.selectBranch (tag)) == (b2 = nodeID.selectBranch (otherItem->getTag ()))) { - // we need a new inner node, since both go on same branch at this level - SHAMapNodeID newNodeID = nodeID.getChildNodeID (b1); - SHAMapTreeNode::pointer newNode = - std::make_shared (mSeq); - newNode->makeInner (); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) - assert (false); - stack.push ({node, nodeID}); - node = newNode; - nodeID = newNodeID; - trackNewNode (node, nodeID); + + // we need a new inner node, since both go on same branch at this level + nodeID = nodeID.getChildNodeID (b1); + node = std::make_shared (mSeq); + node->makeInner (); } // we can add the two leaf nodes here assert (node->isInner ()); - SHAMapNodeID newNodeID = nodeID.getChildNodeID (b1); + SHAMapTreeNode::pointer newNode = std::make_shared (item, type, mSeq); assert (newNode->isValid () && newNode->isLeaf ()); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) + if (!node->setChild (b1, newNode->getNodeHash (), newNode)) + { assert (false); + } - node->setChildHash (b1, newNode->getNodeHash ()); // OPTIMIZEME hash op not needed - trackNewNode (newNode, newNodeID); - newNodeID = nodeID.getChildNodeID (b2); newNode = std::make_shared (otherItem, type, mSeq); assert (newNode->isValid () && newNode->isLeaf ()); - - if (!mTNByID.peekMap().emplace (newNodeID, newNode).second) + if (!node->setChild (b2, newNode->getNodeHash (), newNode)) + { assert (false); - - node->setChildHash (b2, newNode->getNodeHash ()); - trackNewNode (newNode, newNodeID); + } } - dirtyUp (stack, tag, node->getNodeHash ()); + dirtyUp (stack, tag, node); return true; } @@ -870,11 +854,10 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM // can't change the tag but can change the hash uint256 tag = item->getTag (); - ScopedWriteLockType sl (mLock); assert (mState != smsImmutable); - std::stack> stack = - getStack (tag, true); + auto stack = getStack (tag, true); + if (stack.empty ()) throw (std::runtime_error ("missing node")); @@ -888,7 +871,7 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM return false; } - returnNode (node, nodeID, true); + unshareNode (node, nodeID); if (!node->setItem (item, !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM))) @@ -897,7 +880,7 @@ bool SHAMap::updateGiveItem (SHAMapItem::ref item, bool isTransaction, bool hasM return true; } - dirtyUp (stack, tag, node->getNodeHash ()); + dirtyUp (stack, tag, node); return true; } @@ -906,157 +889,6 @@ void SHAMapItem::dump () WriteLog (lsINFO, SHAMap) << "SHAMapItem(" << mTag << ") " << mData.size () << "bytes"; } -SHAMapTreeNode::pointer SHAMap::fetchNodeExternal (const SHAMapNodeID& id, uint256 const& hash) -{ - SHAMapTreeNode::pointer ret = fetchNodeExternalNT (id, hash); - - if (!ret) - throw (SHAMapMissingNode (mType, id, hash)); - - return ret; -} - -// Non-blocking version -SHAMapTreeNode* SHAMap::getNodeAsync ( - const SHAMapNodeID& id, - uint256 const& hash, - SHAMapSyncFilter *filter, - bool& pending) -{ - pending = false; - - // If the node is in mTNByID, return it - SHAMapTreeNode::pointer ptr = mTNByID.retrieve (id); - if (ptr) - return ptr.get (); - - // Try the tree node cache - ptr = getCache (hash); - - if (!ptr) - { - - // Try the filter - if (filter) - { - Blob nodeData; - if (filter->haveNode (id, hash, nodeData)) - { - ptr = std::make_shared ( - nodeData, 0, snfPREFIX, hash, true); - filter->gotNode (true, id, hash, nodeData, ptr->getType ()); - } - } - - if (!ptr) - { - if (mTXMap) - { - // We don't store proposed transaction nodes in the node store - return nullptr; - } - - NodeObject::pointer obj; - - if (!getApp().getNodeStore().asyncFetch (hash, obj)) - { // We would have to block - pending = true; - assert (!obj); - return nullptr; - } - - if (!obj) - return nullptr; - - ptr = std::make_shared (obj->getData(), 0, - snfPREFIX, hash, true); - } - - // Put it in the tree node cache - canonicalize (hash, ptr); - } - - if (id.isRoot ()) - { - // It is legal to replace the root - mTNByID.replace (id, ptr); - root = ptr; - } - else - mTNByID.canonicalize (id, &ptr); - - return ptr.get (); -} - -/** 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 SHAMapNodeID& id, uint256 const& hash) -{ - SHAMapTreeNode::pointer ret; - - // This if allows us to use the SHAMap in unit tests. So we don't attempt - // to fetch external nodes if we're not running in the application. - if (!getApp().running ()) - return ret; - - // Check the cache of shared, immutable tree nodes - ret = getCache (hash); - if (ret) - { // The node was found in the TreeNodeCache - assert (ret->getSeq() == 0); - } - else - { // Check the back end - NodeObject::pointer obj (getApp ().getNodeStore ().fetch (hash)); - if (!obj) - { - if (mLedgerSeq != 0) - { - m_missing_node_handler (mLedgerSeq); - mLedgerSeq = 0; - } - - return ret; - } - - try - { - // We make this node immutable (seq == 0) so that it can be shared - // CoW is needed if it is modified - ret = std::make_shared (obj->getData (), 0, snfPREFIX, hash, true); - - if (ret->getNodeHash () != hash) - { - WriteLog (lsFATAL, SHAMap) << "Hashes don't match"; - assert (false); - return SHAMapTreeNode::pointer (); - } - - // Share this immutable tree node in the TreeNodeCache - canonicalize (hash, ret); - } - catch (...) - { - WriteLog (lsWARNING, SHAMap) << "fetchNodeExternal gets an invalid node: " << hash; - return SHAMapTreeNode::pointer (); - } - } - - if (id.isRoot ()) // it is legal to replace an existing root - { - mTNByID.replace(id, ret); - root = ret; - } - else // Make sure other threads get pointers to the same underlying object - mTNByID.canonicalize (id, &ret); - return ret; -} - bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter) { if (hash == root->getNodeHash ()) @@ -1072,223 +904,247 @@ bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter) WriteLog (lsTRACE, SHAMap) << "Fetch root SHAMap node " << hash; } - SHAMapTreeNode::pointer newRoot = fetchNodeExternalNT(SHAMapNodeID(), hash); + SHAMapTreeNode::pointer newRoot = fetchNodeNT (SHAMapNodeID(), hash, filter); if (newRoot) { root = newRoot; - } - else - { - Blob nodeData; - - if (!filter || !filter->haveNode (SHAMapNodeID (), hash, nodeData)) - return false; - - root = std::make_shared (nodeData, - mSeq - 1, snfPREFIX, hash, true); - filter->gotNode (true, SHAMapNodeID (), hash, nodeData, root->getType ()); + assert (root->getNodeHash () == hash); + return true; } - mTNByID.replace(SHAMapNodeID (), root); - - assert (root->getNodeHash () == hash); - return true; + return false; } -/** Begin saving dirty nodes to be written later */ -int SHAMap::armDirty () +// Replace a node with a shareable node. +// +// This code handles two cases: +// +// 1) An unshared, unshareable node needs to be made shareable +// so immutable SHAMap's can have references to it. +// +// 2) An unshareable node is shared. This happens when you make +// a mutable snapshot of a mutable SHAMap. +void SHAMap::writeNode ( + NodeObjectType t, std::uint32_t seq, SHAMapTreeNode::pointer& node) { - mDirtyNodes = std::make_shared (); - return ++mSeq; + // Node is ours, so we can just make it shareable + assert (node->getSeq() == mSeq); + assert (mBacked); + node->setSeq (0); + + canonicalize (node->getNodeHash(), node); + + Serializer s; + node->addRaw (s, snfPREFIX); + getApp().getNodeStore().store (t, seq, + std::move (s.modData ()), node->getNodeHash ()); } -/** Write all modified nodes to the node store */ -int -SHAMap::flushDirty (DirtySet& set, int maxNodes, NodeObjectType t, std::uint32_t seq) +// We can't modify an inner node someone else might have a +// pointer to because flushing modifies inner nodes -- it +// makes them point to canonical/shared nodes. +void SHAMap::preFlushNode (SHAMapTreeNode::pointer& node) +{ + // A shared node should never need to be flushed + // because that would imply someone modified it + assert (node->getSeq() != 0); + + if (node->getSeq() != mSeq) + { + // Node is not uniquely ours, so unshare it before + // possibly modifying it + node = std::make_shared (*node, mSeq); + } +} + +int SHAMap::unshare () +{ + return walkSubTree (false, hotUNKNOWN, 0); +} + +/** Convert all modified nodes to shared nodes */ +// If requested, write them to the node store +int SHAMap::flushDirty (NodeObjectType t, std::uint32_t seq) +{ + return walkSubTree (true, t, seq); +} + +int SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq) { int flushed = 0; Serializer s; - ScopedWriteLockType sl (mLock); + if (!root || (root->getSeq() == 0) || root->isEmpty ()) + return flushed; - for (DirtySet::iterator it = set.begin (); it != set.end (); it = set.erase (it)) - { - SHAMapNodeID nodeID = *it; - SHAMapTreeNode::pointer node = checkCacheNode (nodeID); - - // Check if node was deleted - if (!node) - continue; - - uint256 const nodeHash = node->getNodeHash(); - - s.erase (); - node->addRaw (s, snfPREFIX); - -#ifdef BEAST_DEBUG - - if (s.getSHA512Half () != nodeHash) - { - WriteLog (lsFATAL, SHAMap) << nodeID; - WriteLog (lsFATAL, SHAMap) << beast::lexicalCast (s.getDataLength ()); - WriteLog (lsFATAL, SHAMap) << s.getSHA512Half () << " != " << nodeHash; - assert (false); - } - -#endif - - if (node->getSeq () != 0) - { - // Node is not shareable - // Make and share a shareable copy - node = std::make_shared (*node, 0); - canonicalize (node->getNodeHash(), node); - mTNByID.replace (nodeID, node); - } - - getApp().getNodeStore ().store (t, seq, std::move (s.modData ()), nodeHash); - - if (flushed++ >= maxNodes) - return flushed; + if (root->isLeaf()) + { // special case -- root is leaf + preFlushNode (root); + if (doWrite && mBacked) + writeNode (t, seq, root); + return 1; } + // Stack of {parent,index,child} pointers representing + // inner nodes we are in the process of flushing + using StackEntry = std::pair ; + std::stack > stack; + + SHAMapTreeNode::pointer node = root; + preFlushNode (node); + + int pos = 0; + + // We can't flush an inner node until we flush its children + while (1) + { + while (pos < 16) + { + if (node->isEmptyBranch (pos)) + { + ++pos; + } + else + { + // No need to do I/O. If the node isn't linked, + // it can't need to be flushed + int branch = pos; + SHAMapTreeNode::pointer child = node->getChild (pos++); + + if (child && (child->getSeq() != 0)) + { + // This is a node that needs to be flushed + + if (child->isInner ()) + { + // save our place and work on this node + preFlushNode (child); + + stack.emplace (std::move (node), branch); + + node = std::move (child); + pos = 0; + } + else + { + // flush this leaf + ++flushed; + + preFlushNode (child); + + assert (node->getSeq() == mSeq); + + if (doWrite && mBacked) + writeNode (t, seq, child); + + node->shareChild (branch, child); + } + } + } + } + + // This inner node can now be shared + if (doWrite && mBacked) + writeNode (t, seq, node); + + ++flushed; + + if (stack.empty ()) + break; + + SHAMapTreeNode::pointer parent = std::move (stack.top().first); + pos = stack.top().second; + stack.pop(); + + // Hook this inner node to its parent + assert (parent->getSeq() == mSeq); + parent->shareChild (pos, node); + + // Continue with parent's next child, if any + node = std::move (parent); + ++pos; + } + + // Last inner node is the new root + root = std::move (node); + return flushed; } -/** Stop saving dirty nodes */ -std::shared_ptr SHAMap::disarmDirty () -{ - ScopedWriteLockType sl (mLock); - - std::shared_ptr ret; - ret.swap (mDirtyNodes); - return ret; -} - -SHAMapTreeNode::pointer SHAMap::getNode (const SHAMapNodeID& nodeID) -{ - - SHAMapTreeNode::pointer node = checkCacheNode (nodeID); - - if (node) - return node; - - node = root; - SHAMapNodeID currentID; - while (nodeID != currentID) - { - int branch = currentID.selectBranch (nodeID.getNodeID ()); - assert (branch >= 0); - uint256 currentHash; - if (!node->descend (branch, currentID, currentHash)) - return SHAMapTreeNode::pointer (); - node = getNode (currentID, currentHash, false); - assert (node); - } - - return node; -} - -// This function returns NULL if no node with that ID exists in the map -// It throws if the map is incomplete -SHAMapTreeNode* SHAMap::getNodePointer (const SHAMapNodeID& nodeID) -{ - SHAMapTreeNode::pointer nodeptr = mTNByID.retrieve (nodeID); - if (nodeptr) - { - SHAMapTreeNode* ret = nodeptr.get (); - ret->touch(mSeq); - return ret; - } - - SHAMapTreeNode* node = root.get(); - SHAMapNodeID currentID; - while (nodeID != currentID) - { - if (node->isLeaf ()) - return nullptr; - - int branch = currentID.selectBranch (nodeID.getNodeID ()); - assert (branch >= 0); - uint256 currentHash; - if (!node->descend (branch, currentID, currentHash)) - return nullptr; - node = getNodePointer (currentID, currentHash); - assert (node); - } - - return node; -} - bool SHAMap::getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format) { // Return the path of nodes to the specified index in the specified format // Return value: true = node present, false = node not present - ScopedReadLockType sl (mLock); - SHAMapTreeNode* inNode = root.get (); SHAMapNodeID nodeID; - while (!inNode->isLeaf ()) + + while (inNode->isInner ()) { Serializer s; inNode->addRaw (s, format); nodes.push_back (s.peekData ()); int branch = nodeID.selectBranch (index); - uint256 nodeHash; - if (!inNode->descend (branch, nodeID, nodeHash)) // paths leads to empty branch + if (inNode->isEmptyBranch (branch)) return false; - inNode = getNodePointer (nodeID, nodeHash); - assert (inNode); + inNode = descendThrow (inNode, branch); + nodeID = nodeID.getChildNodeID (branch); } if (inNode->getTag () != index) // path leads to different leaf return false; - // path lead to the requested leaf + // path leads to the requested leaf Serializer s; inNode->addRaw (s, format); - nodes.push_back (s.peekData ()); + nodes.push_back (std::move(s.peekData ())); return true; } -void SHAMap::dropCache () -{ - ScopedWriteLockType sl (mLock); - assert (mState == smsImmutable); - - mTNByID.clear (); - - if (root) - mTNByID.canonicalize(SHAMapNodeID{}, &root); -} - -void SHAMap::dropBelow (SHAMapTreeNode* d, SHAMapNodeID nodeID) -{ - if (d->isInner ()) - { - for (int i = 0 ; i < 16; ++i) - { - if (!d->isEmptyBranch (i)) - mTNByID.erase (nodeID.getChildNodeID (i)); - } - } -} - void SHAMap::dump (bool hash) { + int leafCount = 0; WriteLog (lsINFO, SHAMap) << " MAP Contains"; - ScopedWriteLockType sl (mLock); - for (auto const& p : mTNByID.peekMap()) + std::stack > stack; + stack.push ({root.get (), SHAMapNodeID ()}); + + do { - WriteLog (lsINFO, SHAMap) << p.second->getString (p.first); - CondLog (hash, lsINFO, SHAMap) << p.second->getNodeHash (); - } + SHAMapTreeNode* node = stack.top().first; + SHAMapNodeID nodeID = stack.top().second; + stack.pop(); + WriteLog (lsINFO, SHAMap) << node->getString (nodeID); + if (hash) + { + WriteLog (lsINFO, SHAMap) << "Hash: " << node->getNodeHash(); + } + + if (node->isInner ()) + { + for (int i = 0; i < 16; ++i) + { + if (!node->isEmptyBranch (i)) + { + SHAMapTreeNode* child = node->getChildPointer (i); + if (child) + { + assert (child->getNodeHash() == node->getChildHash (i)); + stack.push ({child, nodeID.getChildNodeID (i)}); + } + } + } + } + else + ++leafCount; + } + while (!stack.empty ()); + + WriteLog (lsINFO, SHAMap) << leafCount << " resident leaves"; } SHAMapTreeNode::pointer SHAMap::getCache (uint256 const& hash) @@ -1300,9 +1156,12 @@ SHAMapTreeNode::pointer SHAMap::getCache (uint256 const& hash) void SHAMap::canonicalize (uint256 const& hash, SHAMapTreeNode::pointer& node) { + assert (mBacked); assert (node->getSeq() == 0); + assert (node->getNodeHash() == hash); mTreeNodeCache.canonicalize (hash, node); + } //------------------------------------------------------------------------------ @@ -1362,7 +1221,9 @@ public: unexpected (i, "bad traverse"); sMap.addItem (i4, true, false); + sMap.delItem (i2.getTag ()); + sMap.addItem (i3, true, false); i = sMap.peekFirstItem (); diff --git a/src/ripple/app/shamap/SHAMap.h b/src/ripple/app/shamap/SHAMap.h index 5736548a0..4e8f7683f 100644 --- a/src/ripple/app/shamap/SHAMap.h +++ b/src/ripple/app/shamap/SHAMap.h @@ -114,11 +114,8 @@ public: typedef std::pair DeltaRef; typedef std::map Delta; typedef hash_map NodeMap; - typedef hash_set DirtySet; - typedef boost::shared_mutex LockType; - typedef boost::shared_lock ScopedReadLockType; - typedef boost::unique_lock ScopedWriteLockType; + typedef std::stack> 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 visitNodes (std::function const&); + void visitLeaves(std::function const&); // comparison/sync functions void getMissingNodes (std::vector& nodeIDs, std::vector& hashes, int max, @@ -191,11 +183,11 @@ public: std::list& rawNode, bool fatRoot, bool fatLeaves); bool getRootNode (Serializer & s, SHANodeFormat format); std::vector 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 disarmDirty (); + int flushDirty (NodeObjectType t, std::uint32_t seq); + int unshare (); void walkMap (std::vector& missingNodes, int maxMissing); @@ -238,81 +228,105 @@ public: void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function); - 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 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>& stack, - uint256 const& target, uint256 prevHash); - std::stack> + // 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 + 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& 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 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; }; diff --git a/src/ripple/app/shamap/SHAMapDelta.cpp b/src/ripple/app/shamap/SHAMapDelta.cpp index b8ea00b65..05883b764 100644 --- a/src/ripple/app/shamap/SHAMapDelta.cpp +++ b/src/ripple/app/shamap/SHAMapDelta.cpp @@ -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> nodeStack; - nodeStack.push ({node, nodeID}); + std::stack > 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 nodeStack; // track nodes we've pushed - - ScopedReadLockType sl (mLock); + using StackEntry = std::pair ; + std::stack > 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& missingNodes, int maxMissing) { - std::stack> nodeStack; - - ScopedReadLockType sl (mLock); + std::stack > 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; + } + } } } } diff --git a/src/ripple/app/shamap/SHAMapMissingNode.cpp b/src/ripple/app/shamap/SHAMapMissingNode.cpp index 4a7c30bbc..c5d28b648 100644 --- a/src/ripple/app/shamap/SHAMapMissingNode.cpp +++ b/src/ripple/app/shamap/SHAMapMissingNode.cpp @@ -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; }; diff --git a/src/ripple/app/shamap/SHAMapMissingNode.h b/src/ripple/app/shamap/SHAMapMissingNode.h index ab4398caa..8fefd1c1a 100644 --- a/src/ripple/app/shamap/SHAMapMissingNode.h +++ b/src/ripple/app/shamap/SHAMapMissingNode.h @@ -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; }; diff --git a/src/ripple/app/shamap/SHAMapNodeID.cpp b/src/ripple/app/shamap/SHAMapNodeID.cpp index d6e725e32..8c90003ba 100644 --- a/src/ripple/app/shamap/SHAMapNodeID.cpp +++ b/src/ripple/app/shamap/SHAMapNodeID.cpp @@ -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); diff --git a/src/ripple/app/shamap/SHAMapSync.cpp b/src/ripple/app/shamap/SHAMapSync.cpp index 7bc1cdb4f..282443e30 100644 --- a/src/ripple/app/shamap/SHAMapSync.cpp +++ b/src/ripple/app/shamap/SHAMapSync.cpp @@ -26,46 +26,52 @@ namespace ripple { static const uint256 uZero; -void SHAMap::visitLeaves (std::function function) +static void visitLeavesHelper ( + std::function 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& function) +void SHAMap::visitLeaves (std::function const& leafFunction) { + visitNodes (std::bind (visitLeavesHelper, + std::cref (leafFunction), std::placeholders::_1)); +} + +void SHAMap::visitNodes(std::function 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> stack; - SHAMapTreeNode* node = root.get (); - SHAMapNodeID nodeID; + if (!root->isInner ()) + return; + + using StackEntry = std::pair ; + std::stack > 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& 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& 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& fu void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vector& 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& nodeIDs, std::vector> deferredReads; + std::vector > deferredReads; deferredReads.reserve (maxDefer + 16); - std::stack > - stack; + using StackEntry = std::tuple; + std::stack > stack; + // Traverse the map without blocking SHAMapTreeNode *node = root.get (); @@ -164,11 +162,11 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vectorgetChildHash (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& nodeIDs, std::vector& nodeIDs, std::vectorsetFullBelow (); - if (mType == smtSTATE) + if (mBacked) m_fullBelowCache.insert (node->getNodeHash ()); } @@ -238,10 +236,19 @@ void SHAMap::getMissingNodes (std::vector& nodeIDs, std::vector(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& nodeIDs std::list& 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& 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& 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& 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 (rootNode, mSeq - 1, + std::make_shared (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 (rootNode, mSeq - 1, + std::make_shared (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 (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> stack; - ScopedReadLockType sl (mLock); + std::stack > 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 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> stack; + using StackEntry = std::pair ; + std::stack > 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 SHAMap::getTrustedPath (uint256 const& index) +std::list SHAMap::getTrustedPath (uint256 const& index) { - ScopedReadLockType sl (mLock); - - std::stack> 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; } diff --git a/src/ripple/app/shamap/SHAMapTreeNode.cpp b/src/ripple/app/shamap/SHAMapTreeNode.cpp index 01da8fe25..471b3e75d 100644 --- a/src/ripple/app/shamap/SHAMapTreeNode.cpp +++ b/src/ripple/app/shamap/SHAMapTreeNode.cpp @@ -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 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 lock (childLock); + return mChildren[branch].get (); } +SHAMapTreeNode::pointer SHAMapTreeNode::getChild (int branch) +{ + assert (branch >= 0 && branch < 16); + assert (isInnerNode ()); + + std::unique_lock 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 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 diff --git a/src/ripple/app/shamap/SHAMapTreeNode.h b/src/ripple/app/shamap/SHAMapTreeNode.h index b3080dc5c..0d2241df3 100644 --- a/src/ripple/app/shamap/SHAMapTreeNode.h +++ b/src/ripple/app/shamap/SHAMapTreeNode.h @@ -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 const& child); + + // We are sharing/unsharing the child + void shareChild (int m, std::shared_ptr 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 ; diff --git a/src/ripple/app/tx/TransactionAcquire.cpp b/src/ripple/app/tx/TransactionAcquire.cpp index f7fa5b32a..23d72ae01 100644 --- a/src/ripple/app/tx/TransactionAcquire.cpp +++ b/src/ripple/app/tx/TransactionAcquire.cpp @@ -37,7 +37,7 @@ TransactionAcquire::TransactionAcquire (uint256 const& hash, clock_type& clock) Application& app = getApp(); mMap = std::make_shared (smtTRANSACTION, hash, app.getFullBelowCache (), app.getTreeNodeCache()); - mMap->setTXMap (); + mMap->setUnbacked (); } TransactionAcquire::~TransactionAcquire ()