Browse Source

Invariant utilities.

cl-refactor
Gav Wood 10 years ago
parent
commit
1aa1950d75
  1. 8
      libdevcore/Common.cpp
  2. 37
      libdevcore/Common.h
  3. 1
      libdevcore/Exceptions.h
  4. 26
      libethereum/BlockQueue.cpp
  5. 8
      libethereum/BlockQueue.h
  6. 3
      libethereum/Client.cpp

8
libdevcore/Common.cpp

@ -20,7 +20,7 @@
*/ */
#include "Common.h" #include "Common.h"
#include "Exceptions.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -29,5 +29,11 @@ namespace dev
char const* Version = "0.9.14"; char const* Version = "0.9.14";
void HasInvariants::checkInvariants() const
{
if (!invariants())
BOOST_THROW_EXCEPTION(FailedInvariant());
}
} }

37
libdevcore/Common.h

@ -128,14 +128,46 @@ inline N diff(N const& _a, N const& _b)
} }
/// RAII utility class whose destructor calls a given function. /// RAII utility class whose destructor calls a given function.
class ScopeGuard { class ScopeGuard
{
public: public:
ScopeGuard(std::function<void(void)> _f): m_f(_f) {} ScopeGuard(std::function<void(void)> _f): m_f(_f) {}
~ScopeGuard() { m_f(); } ~ScopeGuard() { m_f(); }
private: private:
std::function<void(void)> m_f; std::function<void(void)> m_f;
}; };
/// Inheritable for classes that have invariants.
class HasInvariants
{
public:
/// Check invariants are met, throw if not.
void checkInvariants() const;
protected:
/// Reimplement to specify the invariants.
virtual bool invariants() const = 0;
};
/// RAII checker for invariant assertions.
class InvariantChecker
{
public:
InvariantChecker(HasInvariants* _this): m_this(_this) { m_this->checkInvariants(); }
~InvariantChecker() { m_this->checkInvariants(); }
private:
HasInvariants const* m_this;
};
/// Scope guard for invariant check in a class derived from HasInvariants.
#if ETH_DEBUG
#define DEV_INVARIANT_CHECK ::dev::InvariantChecker __dev_invariantCheck(this)
#else
#define DEV_INVARIANT_CHECK (void)0;
#endif
enum class WithExisting: int enum class WithExisting: int
{ {
Trust = 0, Trust = 0,
@ -145,7 +177,8 @@ enum class WithExisting: int
} }
namespace std { namespace std
{
inline dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b) inline dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b)
{ {

1
libdevcore/Exceptions.h

@ -53,6 +53,7 @@ struct BadRoot: virtual Exception {};
struct FileError: virtual Exception {}; struct FileError: virtual Exception {};
struct Overflow: virtual Exception {}; struct Overflow: virtual Exception {};
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} }; struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} };
struct FailedInvariant: virtual Exception {};
// error information to be added to exceptions // error information to be added to exceptions
using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>; using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;

26
libethereum/BlockQueue.cpp

@ -74,7 +74,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
} }
UpgradeGuard ul(l); UpgradeGuard ul(l);
invariants_WITH_LOCK(); DEV_INVARIANT_CHECK;
// Check it's not in the future // Check it's not in the future
(void)_isOurs; (void)_isOurs;
@ -86,7 +86,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (strftime(buf, 24, "%X", localtime(&bit)) == 0) if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf; cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
invariants_WITH_LOCK();
return ImportResult::FutureTime; return ImportResult::FutureTime;
} }
else else
@ -96,7 +95,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
{ {
m_knownBad.insert(bi.hash()); m_knownBad.insert(bi.hash());
// bad parent; this is bad too, note it as such // bad parent; this is bad too, note it as such
invariants_WITH_LOCK();
return ImportResult::BadChain; return ImportResult::BadChain;
} }
else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash))
@ -106,7 +104,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h); m_unknownSet.insert(h);
invariants_WITH_LOCK();
return ImportResult::UnknownParent; return ImportResult::UnknownParent;
} }
else else
@ -115,7 +112,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
cblockq << "OK - ready for chain insertion."; cblockq << "OK - ready for chain insertion.";
m_ready.push_back(make_pair(h, _block.toBytes())); m_ready.push_back(make_pair(h, _block.toBytes()));
m_readySet.insert(h); m_readySet.insert(h);
invariants_WITH_LOCK();
noteReady_WITH_LOCK(h); noteReady_WITH_LOCK(h);
m_onReady(); m_onReady();
@ -127,7 +123,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
bool BlockQueue::doneDrain(h256s const& _bad) bool BlockQueue::doneDrain(h256s const& _bad)
{ {
WriteGuard l(m_lock); WriteGuard l(m_lock);
invariants_WITH_LOCK(); DEV_INVARIANT_CHECK;
m_drainingSet.clear(); m_drainingSet.clear();
if (_bad.size()) if (_bad.size())
{ {
@ -146,8 +142,6 @@ bool BlockQueue::doneDrain(h256s const& _bad)
} }
} }
m_knownBad += _bad; m_knownBad += _bad;
// GAA!!!! NEVER EMPTY?!?!?! TODO: remove items from readySet!
invariants_WITH_LOCK();
return !m_readySet.empty(); return !m_readySet.empty();
} }
@ -169,12 +163,11 @@ void BlockQueue::tick(BlockChain const& _bc)
{ {
UpgradeGuard l2(l); UpgradeGuard l2(l);
invariants_WITH_LOCK(); DEV_INVARIANT_CHECK;
auto end = m_future.lower_bound(t); auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i) for (auto i = m_future.begin(); i != end; ++i)
todo.push_back(move(i->second)); todo.push_back(move(i->second));
m_future.erase(m_future.begin(), end); m_future.erase(m_future.begin(), end);
invariants_WITH_LOCK();
} }
} }
cblockq << "Importing" << todo.size() << "past-future blocks."; cblockq << "Importing" << todo.size() << "past-future blocks.";
@ -207,7 +200,7 @@ QueueStatus BlockQueue::blockStatus(h256 const& _h) const
void BlockQueue::drain(std::vector<bytes>& o_out, unsigned _max) void BlockQueue::drain(std::vector<bytes>& o_out, unsigned _max)
{ {
WriteGuard l(m_lock); WriteGuard l(m_lock);
invariants_WITH_LOCK(); DEV_INVARIANT_CHECK;
if (m_drainingSet.empty()) if (m_drainingSet.empty())
{ {
o_out.resize(min<unsigned>(_max, m_ready.size())); o_out.resize(min<unsigned>(_max, m_ready.size()));
@ -224,17 +217,16 @@ void BlockQueue::drain(std::vector<bytes>& o_out, unsigned _max)
// swap(o_out, m_ready); // swap(o_out, m_ready);
// swap(m_drainingSet, m_readySet); // swap(m_drainingSet, m_readySet);
} }
invariants_WITH_LOCK();
} }
void BlockQueue::invariants_WITH_LOCK() const bool BlockQueue::invariants() const
{ {
assert(m_readySet.size() == m_ready.size()); return m_readySet.size() == m_ready.size();
} }
void BlockQueue::noteReady_WITH_LOCK(h256 const& _good) void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
{ {
invariants_WITH_LOCK(); DEV_INVARIANT_CHECK;
list<h256> goodQueue(1, _good); list<h256> goodQueue(1, _good);
while (!goodQueue.empty()) while (!goodQueue.empty())
{ {
@ -250,12 +242,11 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
} }
m_unknown.erase(r.first, r.second); m_unknown.erase(r.first, r.second);
} }
invariants_WITH_LOCK();
} }
void BlockQueue::retryAllUnknown() void BlockQueue::retryAllUnknown()
{ {
invariants_WITH_LOCK(); DEV_INVARIANT_CHECK;
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{ {
m_ready.push_back(it->second); m_ready.push_back(it->second);
@ -264,5 +255,4 @@ void BlockQueue::retryAllUnknown()
m_readySet.insert(newReady); m_readySet.insert(newReady);
} }
m_unknown.clear(); m_unknown.clear();
invariants_WITH_LOCK();
} }

8
libethereum/BlockQueue.h

@ -30,6 +30,7 @@
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
@ -60,7 +61,7 @@ enum class QueueStatus
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method). * Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
* @threadsafe * @threadsafe
*/ */
class BlockQueue class BlockQueue: HasInvariants
{ {
public: public:
/// Import a block into the queue. /// Import a block into the queue.
@ -87,7 +88,7 @@ public:
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); }
/// Clear everything. /// Clear everything.
void clear() { WriteGuard l(m_lock); invariants_WITH_LOCK(); m_readySet.clear(); m_drainingSet.clear(); m_ready.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); invariants_WITH_LOCK(); } void clear() { WriteGuard l(m_lock); DEV_INVARIANT_CHECK; m_readySet.clear(); m_drainingSet.clear(); m_ready.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); }
/// Return first block with an unknown parent. /// Return first block with an unknown parent.
h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); } h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); }
@ -102,7 +103,8 @@ public:
private: private:
void noteReady_WITH_LOCK(h256 const& _b); void noteReady_WITH_LOCK(h256 const& _b);
void invariants_WITH_LOCK() const;
bool invariants() const override;
mutable boost::shared_mutex m_lock; ///< General lock. mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_drainingSet; ///< All blocks being imported. std::set<h256> m_drainingSet; ///< All blocks being imported.

3
libethereum/Client.cpp

@ -582,7 +582,6 @@ void Client::onChainChanged(ImportRoute const& _ir)
m_preMine = newPreMine; m_preMine = newPreMine;
DEV_TIMED(working) ETH_WRITE_GUARDED(x_working) DEV_TIMED(working) ETH_WRITE_GUARDED(x_working)
m_working = newPreMine; m_working = newPreMine;
// Transactions ts = m_postMine.pending();
ETH_READ_GUARDED(x_postMine) ETH_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending()) for (auto const& t: m_postMine.pending())
{ {
@ -663,7 +662,7 @@ void Client::doWork()
bool t = true; bool t = true;
if (m_syncBlockQueue.compare_exchange_strong(t, false)) if (m_syncBlockQueue.compare_exchange_strong(t, false))
syncBlockQueue(); // GAAA!!!!! CALLED TOO OFTEN!!! syncBlockQueue();
t = true; t = true;
if (m_syncTransactionQueue.compare_exchange_strong(t, false) && !m_remoteWorking) if (m_syncTransactionQueue.compare_exchange_strong(t, false) && !m_remoteWorking)

Loading…
Cancel
Save