From 44748a35e8ea217708afefaebb061ae7c4bf25e8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 25 Jul 2014 15:01:00 +0200 Subject: [PATCH] Threadsafe transaction queue. Some repotting. --- libethereum/BlockChain.cpp | 20 +-------- libethereum/BlockChain.h | 58 ++---------------------- libethereum/BlockDetails.cpp | 40 +++++++++++++++++ libethereum/BlockDetails.h | 76 ++++++++++++++++++++++++++++++++ libethereum/Guards.cpp | 29 ++++++++++++ libethereum/Guards.h | 34 ++++++++++++++ libethereum/TransactionQueue.cpp | 8 +++- libethereum/TransactionQueue.h | 29 ++++++------ 8 files changed, 204 insertions(+), 90 deletions(-) create mode 100644 libethereum/BlockDetails.cpp create mode 100644 libethereum/BlockDetails.h create mode 100644 libethereum/Guards.cpp create mode 100644 libethereum/Guards.h diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 21fa9f256..742818118 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -35,10 +35,7 @@ using namespace eth; #define ETH_CATCH 1 -namespace eth -{ - -std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc) +std::ostream& eth::operator<<(std::ostream& _out, BlockChain const& _bc) { string cmp = toBigEndianString(_bc.currentHash()); auto it = _bc.m_extrasDB->NewIterator(_bc.m_readOptions); @@ -51,21 +48,6 @@ std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc) delete it; return _out; } -} - -BlockDetails::BlockDetails(RLP const& _r) -{ - number = _r[0].toInt(); - totalDifficulty = _r[1].toInt(); - parent = _r[2].toHash(); - children = _r[3].toVector(); - bloom = _r[4].toHash(); -} - -bytes BlockDetails::rlp() const -{ - return rlpList(number, totalDifficulty, parent, children, bloom); -} std::map const& eth::genesisState() { diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 1a05d631c..052471537 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -22,64 +22,17 @@ #pragma once #include -#include #include #include #include -#include "Manifest.h" +#include "Guards.h" +#include "BlockDetails.h" #include "AddressState.h" namespace ldb = leveldb; namespace eth { -class RLP; -class RLPStream; - -struct BlockDetails -{ - BlockDetails(): number(0), totalDifficulty(0) {} - BlockDetails(uint _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {} - BlockDetails(RLP const& _r); - bytes rlp() const; - - bool isNull() const { return !totalDifficulty; } - explicit operator bool() const { return !isNull(); } - - uint number; // TODO: remove? - u256 totalDifficulty; - h256 parent; - h256s children; - h256 bloom; -}; - -struct BlockBlooms -{ - BlockBlooms() {} - BlockBlooms(RLP const& _r) { blooms = _r.toVector(); } - bytes rlp() const { RLPStream s; s << blooms; return s.out(); } - - h256s blooms; -}; - -struct BlockTraces -{ - BlockTraces() {} - BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); } - bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); } - - Manifests traces; -}; - - -typedef std::map BlockDetailsHash; -typedef std::map BlockBloomsHash; -typedef std::map BlockTracesHash; - -static const BlockDetails NullBlockDetails; -static const BlockBlooms NullBlockBlooms; -static const BlockTraces NullBlockTraces; - static const h256s NullH256s; class State; @@ -95,16 +48,11 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "= // TODO: Move all this Genesis stuff into Genesis.h/.cpp std::map const& genesisState(); -using ReadGuard = boost::shared_lock; -using UpgradableGuard = boost::upgrade_lock; -using UpgradeGuard = boost::upgrade_to_unique_lock; -using WriteGuard = boost::unique_lock; - ldb::Slice toSlice(h256 _h, unsigned _sub = 0); /** * @brief Implements the blockchain database. All data this gives is disk-backed. - * @todo Make thread-safe. + * @threadsafe * @todo Make not memory hog (should actually act as a cache and deallocate old entries). */ class BlockChain diff --git a/libethereum/BlockDetails.cpp b/libethereum/BlockDetails.cpp new file mode 100644 index 000000000..ae107fa68 --- /dev/null +++ b/libethereum/BlockDetails.cpp @@ -0,0 +1,40 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file BlockDetails.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "BlockDetails.h" + +#include +using namespace std; +using namespace eth; + +BlockDetails::BlockDetails(RLP const& _r) +{ + number = _r[0].toInt(); + totalDifficulty = _r[1].toInt(); + parent = _r[2].toHash(); + children = _r[3].toVector(); + bloom = _r[4].toHash(); +} + +bytes BlockDetails::rlp() const +{ + return rlpList(number, totalDifficulty, parent, children, bloom); +} diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h new file mode 100644 index 000000000..6db2171ee --- /dev/null +++ b/libethereum/BlockDetails.h @@ -0,0 +1,76 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file BlockDetails.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include "Manifest.h" +namespace ldb = leveldb; + +namespace eth +{ + +struct BlockDetails +{ + BlockDetails(): number(0), totalDifficulty(0) {} + BlockDetails(uint _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {} + BlockDetails(RLP const& _r); + bytes rlp() const; + + bool isNull() const { return !totalDifficulty; } + explicit operator bool() const { return !isNull(); } + + uint number; // TODO: remove? + u256 totalDifficulty; + h256 parent; + h256s children; + h256 bloom; +}; + +struct BlockBlooms +{ + BlockBlooms() {} + BlockBlooms(RLP const& _r) { blooms = _r.toVector(); } + bytes rlp() const { RLPStream s; s << blooms; return s.out(); } + + h256s blooms; +}; + +struct BlockTraces +{ + BlockTraces() {} + BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); } + bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); } + + Manifests traces; +}; + + +typedef std::map BlockDetailsHash; +typedef std::map BlockBloomsHash; +typedef std::map BlockTracesHash; + +static const BlockDetails NullBlockDetails; +static const BlockBlooms NullBlockBlooms; +static const BlockTraces NullBlockTraces; + +} diff --git a/libethereum/Guards.cpp b/libethereum/Guards.cpp new file mode 100644 index 000000000..b2e12f98e --- /dev/null +++ b/libethereum/Guards.cpp @@ -0,0 +1,29 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Guards.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Guards.h" +using namespace std; +using namespace eth; + +namespace eth +{ + +} diff --git a/libethereum/Guards.h b/libethereum/Guards.h new file mode 100644 index 000000000..cf047a3b3 --- /dev/null +++ b/libethereum/Guards.h @@ -0,0 +1,34 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Guards.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include + +namespace eth +{ + +using ReadGuard = boost::shared_lock; +using UpgradableGuard = boost::upgrade_lock; +using UpgradeGuard = boost::upgrade_to_unique_lock; +using WriteGuard = boost::unique_lock; + +} diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index f63384926..9a8c4c20a 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -31,6 +31,8 @@ bool TransactionQueue::import(bytesConstRef _block) { // Check if we already know this transaction. h256 h = sha3(_block); + + UpgradableGuard l(x_data); if (m_data.count(h)) return false; @@ -40,10 +42,9 @@ bool TransactionQueue::import(bytesConstRef _block) // The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction). Transaction t(_block); auto s = t.sender(); - if (m_interest.count(s)) - m_interestQueue.push_back(t); // If valid, append to blocks. + UpgradeGuard ul(l); m_data[h] = _block.toBytes(); } catch (InvalidTransactionFormat const& _e) @@ -62,8 +63,10 @@ bool TransactionQueue::import(bytesConstRef _block) void TransactionQueue::setFuture(std::pair const& _t) { + UpgradableGuard l(x_data); if (m_data.count(_t.first)) { + UpgradeGuard ul(l); m_data.erase(_t.first); m_future.insert(make_pair(Transaction(_t.second).sender(), _t)); } @@ -71,6 +74,7 @@ void TransactionQueue::setFuture(std::pair const& _t) void TransactionQueue::noteGood(std::pair const& _t) { + WriteGuard l(x_data); auto r = m_future.equal_range(Transaction(_t.second).sender()); for (auto it = r.first; it != r.second; ++it) m_data.insert(_t); diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 0c52370b3..d3ad354cc 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -21,8 +21,10 @@ #pragma once +#include #include -#include "Transaction.h" +#include "libethcore/CommonEth.h" +#include "Guards.h" namespace eth { @@ -31,28 +33,27 @@ class BlockChain; /** * @brief A queue of Transactions, each stored as RLP. + * @threadsafe */ class TransactionQueue { public: - bool attemptImport(bytesConstRef _block) { try { import(_block); return true; } catch (...) { return false; } } - bool attemptImport(bytes const& _block) { try { import(&_block); return true; } catch (...) { return false; } } - bool import(bytesConstRef _block); - void drop(h256 _txHash) { m_data.erase(_txHash); } - std::map const& transactions() const { return m_data; } + bool attemptImport(bytesConstRef _tx) { try { import(_block); return true; } catch (...) { return false; } } + bool attemptImport(bytes const& _tx) { return attemptImport(&_block); } + bool import(bytesConstRef _tx); + + void drop(h256 _txHash) { WriteGuard l(x_data); m_data.erase(_txHash); } + + std::map transactions() const { ReadGuard l(x_data); return m_data; } void setFuture(std::pair const& _t); void noteGood(std::pair const& _t); - Transactions interestQueue() { Transactions ret; swap(ret, m_interestQueue); return ret; } - void pushInterest(Address _a) { m_interest[_a]++; } - void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); } - private: - std::map m_data; ///< Map of SHA3(tx) to tx. - Transactions m_interestQueue; - std::map m_interest; - std::multimap> m_future; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. + std::map m_data; ///< Map of SHA3(tx) to tx. + boost::shared_mutex x_data; + + std::multimap> m_future; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. }; }