/* 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 DownloadMan.h * @author Gav Wood * @date 2014 */ #pragma once #include #include #include #include #include #include #include namespace dev { namespace eth { class DownloadMan; class DownloadSub { friend class DownloadMan; public: DownloadSub(DownloadMan& _man); ~DownloadSub(); /// Finished last fetch - grab the next bunch of block hashes to download. h256Hash nextFetch(unsigned _n); /// Note that we've received a particular block. @returns true if we had asked for it but haven't received it yet. bool noteBlock(h256 _hash); /// Nothing doing here. void doneFetch() { resetFetch(); } bool askedContains(unsigned _i) const { Guard l(m_fetch); return m_asked.contains(_i); } RangeMask const& asked() const { return m_asked; } RangeMask const& attemped() const { return m_attempted; } private: void resetFetch() // Called by DownloadMan when we need to reset the download. { Guard l(m_fetch); m_remaining.clear(); m_indices.clear(); m_asked.reset(); m_attempted.reset(); } DownloadMan* m_man = nullptr; mutable Mutex m_fetch; h256Hash m_remaining; std::unordered_map m_indices; RangeMask m_asked; RangeMask m_attempted; }; class DownloadMan { friend class DownloadSub; public: struct Overview { size_t total; size_t firstIncomplete; size_t lastComplete; size_t lastStarted; }; ~DownloadMan() { for (auto i: m_subs) i->m_man = nullptr; } void appendToChain(h256s const& _hashes) { WriteGuard l(m_lock); m_chain.insert(m_chain.end(), _hashes.cbegin(), _hashes.cend()); m_blocksGot = RangeMask(0, m_chain.size()); } void resetToChain(h256s const& _chain) { DEV_READ_GUARDED(x_subs) for (auto i: m_subs) i->resetFetch(); WriteGuard l(m_lock); m_chain.clear(); m_chain.reserve(_chain.size()); for (auto i = _chain.rbegin(); i != _chain.rend(); ++i) m_chain.push_back(*i); m_blocksGot = RangeMask(0, m_chain.size()); } void reset() { DEV_READ_GUARDED(x_subs) for (auto i: m_subs) i->resetFetch(); WriteGuard l(m_lock); m_chain.clear(); m_blocksGot.reset(); } RangeMask taken(bool _desperate = false) const { ReadGuard l(m_lock); auto ret = m_blocksGot; if (!_desperate) DEV_READ_GUARDED(x_subs) for (auto i: m_subs) ret += i->m_asked; return ret; } bool isComplete() const { ReadGuard l(m_lock); return m_blocksGot.full(); } h256s remaining() const { h256s ret; DEV_READ_GUARDED(m_lock) for (auto i: m_blocksGot.inverted()) ret.push_back(m_chain[i]); return ret; } h256 firstBlock() const { return m_chain.empty() ? h256() : m_chain[0]; } Overview overview() const; size_t chainSize() const { ReadGuard l(m_lock); return m_chain.size(); } size_t chainEmpty() const { ReadGuard l(m_lock); return m_chain.empty(); } void foreachSub(std::function const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); } unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); } RangeMask blocksGot() const { ReadGuard l(m_lock); return m_blocksGot; } private: mutable SharedMutex m_lock; h256s m_chain; RangeMask m_blocksGot; mutable SharedMutex x_subs; std::unordered_set m_subs; }; class HashDownloadMan; class HashDownloadSub { friend class HashDownloadMan; public: HashDownloadSub(HashDownloadMan& _man); ~HashDownloadSub(); /// Finished last fetch - grab the next hash index to download unsigned nextFetch(unsigned _n); /// Note that we've received a particular hash range. void noteHash(unsigned _index, unsigned count); /// Nothing doing here. void doneFetch() { resetFetch(); } bool askedContains(unsigned _i) const { Guard l(m_fetch); return m_asked.contains(_i); } RangeMask const& asked() const { return m_asked; } private: void resetFetch(); // Called by DownloadMan when we need to reset the download. HashDownloadMan* m_man = nullptr; mutable Mutex m_fetch; unsigned m_remaining; RangeMask m_asked; }; class HashDownloadMan { friend class HashDownloadSub; public: ~HashDownloadMan() { for (auto i: m_subs) i->m_man = nullptr; } void resetToRange(unsigned _start, unsigned _count) { { ReadGuard l(x_subs); for (auto i: m_subs) i->resetFetch(); } WriteGuard l(m_lock); m_chainStart = _start; m_chainCount = _count; m_got += RangeMask(_start, _start + _count); { ReadGuard l(x_subs); for (auto i: m_subs) i->resetFetch(); } } void reset(unsigned _start) { WriteGuard l(m_lock); m_chainStart = _start; m_chainCount = 0; m_got = RangeMask(_start, _start); } RangeMask taken(bool _desperate = false) const { ReadGuard l(m_lock); auto ret = m_got; if (!_desperate) { ReadGuard l(x_subs); for (auto i: m_subs) ret += i->m_asked; } return ret; } bool isComplete() const { ReadGuard l(m_lock); return m_got.full(); } size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; } size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; } void foreachSub(std::function const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); } unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); } RangeMask hashesGot() const { ReadGuard l(m_lock); return m_got; } private: mutable SharedMutex m_lock; unsigned m_chainStart = 0; unsigned m_chainCount = 0; RangeMask m_got; mutable SharedMutex x_subs; std::unordered_set m_subs; }; } }