diff --git a/libethcore/Dagger.cpp b/libethcore/Dagger.cpp index 4b5841d82..8be21bdbb 100644 --- a/libethcore/Dagger.cpp +++ b/libethcore/Dagger.cpp @@ -39,7 +39,7 @@ namespace eth MineInfo Dagger::mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool _continue, bool _turbo) { - MineInfo ret{0.f, 1e99, 0, false}; + MineInfo ret; static std::mt19937_64 s_eng((time(0) + (unsigned)m_last)); u256 s = (m_last = h256::random(s_eng)); diff --git a/libethcore/Dagger.h b/libethcore/Dagger.h index e920549ce..61848f8c9 100644 --- a/libethcore/Dagger.h +++ b/libethcore/Dagger.h @@ -38,10 +38,11 @@ inline uint toLog2(u256 _d) struct MineInfo { - double requirement; - double best; - uint hashes; - bool completed; + void combine(MineInfo const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); hashes += _m.hashes; completed = completed || _m.completed; } + double requirement = 0; + double best = 1e99; + uint hashes = 0; + bool completed = false; }; #if FAKE_DAGGER diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 738beb9be..2ae0d7de4 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -56,9 +56,9 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_bc(_dbPath, !m_vc.ok() || _forceClean), m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)), m_preMine(_us, m_stateDB), - m_postMine(_us, m_stateDB), - m_miner(this) + m_postMine(_us, m_stateDB) { + setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); @@ -115,7 +115,11 @@ void Client::clearPending() appendFromNewPending(m_postMine.bloom(i), changeds); changeds.insert(PendingChangedFilter); m_postMine = m_preMine; - m_miner.noteStateChange(); + { + ReadGuard l(x_miners); + for (auto& m: m_miners) + m.noteStateChange(); + } noteChanged(changeds); } @@ -289,6 +293,46 @@ void Client::connect(std::string const& _seedHost, unsigned short _port) m_net->connect(_seedHost, _port); } +void Client::setMiningThreads(unsigned _threads) +{ + stopMining(); + + auto t = _threads ? _threads : thread::hardware_concurrency(); + WriteGuard l(x_miners); + m_miners.clear(); + m_miners.resize(t); + unsigned i = 0; + for (auto& m: m_miners) + m.setup(this, i++); +} + +MineProgress Client::miningProgress() const +{ + MineProgress ret; + ReadGuard l(x_miners); + for (auto& m: m_miners) + ret.combine(m.miningProgress()); + return ret; +} + +std::list Client::miningHistory() +{ + std::list ret; + ReadGuard l(x_miners); + if (m_miners.empty()) + return ret; + ret = m_miners[0].miningHistory(); + for (unsigned i = 1; i < m_miners.size(); ++i) + { + auto l = m_miners[i].miningHistory(); + auto ri = ret.begin(); + auto li = l.begin(); + for (; ri != ret.end() && li != l.end(); ++ri, ++li) + ri->combine(*li); + } + return ret; +} + void Client::setupState(State& _s) { { @@ -410,23 +454,26 @@ void Client::work() cworkin << "WORK"; h256Set changeds; - if (m_miner.isComplete()) - { - cwork << "CHAIN <== postSTATE"; - h256s hs; - { - WriteGuard l(x_stateDB); - hs = m_bc.attemptImport(m_miner.blockData(), m_stateDB); - } - if (hs.size()) + ReadGuard l(x_miners); + for (auto& m: m_miners) + if (m.isComplete()) { - for (auto h: hs) - appendFromNewBlock(h, changeds); - changeds.insert(ChainChangedFilter); - //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. + cwork << "CHAIN <== postSTATE"; + h256s hs; + { + WriteGuard l(x_stateDB); + hs = m_bc.attemptImport(m.blockData(), m_stateDB); + } + if (hs.size()) + { + for (auto h: hs) + appendFromNewBlock(h, changeds); + changeds.insert(ChainChangedFilter); + //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. + } + for (auto& m: m_miners) + m.noteStateChange(); } - m_miner.noteStateChange(); - } // Synchronise state to block chain. // This should remove any transactions on our queue that are included within our state. @@ -435,6 +482,7 @@ void Client::work() // if there are no checkpoints before our fork) reverting to the genesis block and replaying // all blocks. // Resynchronise state with block chain & trans + bool rsm = false; { WriteGuard l(x_stateDB); cwork << "BQ ==> CHAIN ==> STATE"; @@ -451,8 +499,6 @@ void Client::work() if (newBlocks.size()) m_stateDB = db; - bool rsm = false; - cwork << "preSTATE <== CHAIN"; if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { @@ -474,9 +520,12 @@ void Client::work() cnote << "Additional transaction ready: Restarting mining operation."; rsm = true; } - - if (rsm) - m_miner.noteStateChange(); + } + if (rsm) + { + ReadGuard l(x_miners); + for (auto& m: m_miners) + m.noteStateChange(); } cwork << "noteChanged" << changeds.size() << "items"; diff --git a/libethereum/Client.h b/libethereum/Client.h index 4932097ac..b62a9066f 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -217,32 +217,39 @@ public: bool miningParanoia() const { return m_paranoia; } /// Change whether we check block validity prior to mining. void setParanoia(bool _p) { m_paranoia = _p; } + /// Should we force mining to happen, even without transactions? + bool forceMining() const { return m_forceMining; } + /// Enable/disable forcing of mining to happen, even without transactions. + void setForceMining(bool _enable) { m_forceMining = _enable; } + /// Are we mining as fast as we can? + bool turboMining() const { return m_turboMining; } + /// Enable/disable fast mining. + void setTurboMining(bool _enable = true) { m_turboMining = _enable; } + /// Set the coinbase address. void setAddress(Address _us) { m_preMine.setAddress(_us); } /// Get the coinbase address. Address address() const { return m_preMine.address(); } + /// Stops mining and sets the number of mining threads (0 for automatic). + void setMiningThreads(unsigned _threads = 0); + /// Get the effective number of mining threads. + unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); } /// Start mining. - /// NOT thread-safe - void startMining() { m_miner.start(); ensureWorking(); } + /// NOT thread-safe - call it & stopMining only from a single thread + void startMining() { ensureWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } /// Stop mining. /// NOT thread-safe - void stopMining() { m_miner.stop(); } + void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); } /// Are we mining now? - bool isMining() { return m_miner.isRunning(); } + bool isMining() { ReadGuard l(x_miners); return m_miners.size() && m_miners[0].isRunning(); } /// Check the progress of the mining. - MineProgress miningProgress() const { return m_miner.miningProgress(); } + MineProgress miningProgress() const; /// Get and clear the mining history. - std::list miningHistory() { return m_miner.miningHistory(); } - - bool forceMining() const { return m_forceMining; } - void setForceMining(bool _enable) { m_forceMining = _enable; } + std::list miningHistory(); /// Clears pending transactions. Just for debug use. void clearPending(); - void setTurboMining(bool _enable = true) { m_turboMining = _enable; } - bool turboMining() const { return m_turboMining; } - private: /// Ensure the worker thread is running. Needed for blockchain maintenance & mining. void ensureWorking(); @@ -296,7 +303,8 @@ private: std::unique_ptr m_work; ///< The work thread. std::atomic m_workState; - Miner m_miner; + std::vector m_miners; + mutable boost::shared_mutex x_miners; bool m_paranoia = false; ///< Should we be paranoid about our state? bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. bool m_forceMining = false; ///< Mine even when there are no transactions pending? diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index 6fe714a5e..3c0a8b003 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -33,6 +33,9 @@ Miner::Miner(MinerHost* _host, unsigned _id): void Miner::start() { + if (!m_host) + return; + Guard l(x_work); if (!m_work) { diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 7ddd54ea6..c0aa09d56 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -37,11 +37,12 @@ namespace eth */ struct MineProgress { - double requirement; ///< The PoW requirement - as the second logarithm of the minimum acceptable hash. - double best; ///< The PoW achievement - as the second logarithm of the minimum found hash. - double current; ///< The most recent PoW achievement - as the second logarithm of the presently found hash. - uint hashes; ///< Total number of hashes computed. - uint ms; ///< Total number of milliseconds of mining thus far. + void combine(MineProgress const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); current = std::max(current, _m.current); hashes += _m.hashes; ms = std::max(ms, _m.ms); } + double requirement = 0; ///< The PoW requirement - as the second logarithm of the minimum acceptable hash. + double best = 1e99; ///< The PoW achievement - as the second logarithm of the minimum found hash. + double current = 0; ///< The most recent PoW achievement - as the second logarithm of the presently found hash. + uint hashes = 0; ///< Total number of hashes computed. + uint ms = 0; ///< Total number of milliseconds of mining thus far. }; /** @@ -74,12 +75,24 @@ public: class Miner { public: - /// Constructor. Starts miner. + /// Null constructor. + Miner(): m_host(nullptr), m_id(0) {} + + /// Constructor. Miner(MinerHost* _host, unsigned _id = 0); + /// Move-constructor. + Miner(Miner&& _m) { std::swap(m_host, _m.m_host); std::swap(m_id, _m.m_id); } + + /// Move-assignment. + Miner& operator=(Miner&& _m) { std::swap(m_host, _m.m_host); std::swap(m_id, _m.m_id); return *this; } + /// Destructor. Stops miner. ~Miner() { stop(); } + /// Setup its basics. + void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; m_id = _id; } + /// Start mining. void start(); @@ -108,8 +121,8 @@ private: /// Do some work on the mining. void work(); - MinerHost* m_host; ///< Our host. - unsigned m_id; ///< Our identity. + MinerHost* m_host = nullptr; ///< Our host. + unsigned m_id = 0; ///< Our identity. std::mutex x_work; ///< Mutex protecting the creation of the work thread. std::unique_ptr m_work; ///< The work thread.